【題解】Codeforces 633C. Spy Syndrome2 AC自動機

給出句子的加密規則:

  1. 字母全部變成小寫
  2. 反轉每個單詞
  3. 移除所有空格。

現在給定一個加密後的句子(1e4)(1e4),一個可用的單詞表(1e5,1e6)(1e5個,總長總長不超過1e6),輸出任意一個加密前的句子,保證有解。


把單詞小寫後反轉插入到AC自動機中,現在只需要把文本串拆分成模式串。

f[i]f[i]表示[1,i][1,i]能否被拆分完畢,將文本串輸入到AC自動機中,輸入到ii位置時枚舉所有匹配的文本串,如果有一個能轉移到f[j]=1f[j]=1,就記錄這個匹配。

最後按轉移輸出句子即可。

AC自動機構建複雜度1e6261e6*26,匹配複雜度1e41e61e4*\sqrt{1e6},總複雜度4e74e7左右。


很經典的AC自動機題目.

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 1000016, MOD = 1000000007;

int dp[M], tr[M];
string pat[M]; //模式串

struct Aho_Corasick_Automaton
{
    //Trie樹(圖)/是否爲終結點/失配鏈接/後綴鏈接,都是節點的屬性
    int sz = 0;
    int ch[M][26], end[M], fail[M], last[M];
    int ans[M]; //多模式匹配,終結節點的出現次數
    
    int init(int id = 0)
    {
        memset(ch[id], 0, sizeof(ch[0]));
        end[id] = fail[id] = last[id] = ans[id] = 0;
        return sz = id;
    }

    // 向Trie樹中嘗試插入一個模式串, 返回插入後的節點編號
    void insert(const char *s, int id)
    {
    	//printf("ac insert %s\n",s );
        int u = 0;
        for(int i = 0; s[i]; i++)
        {
            int &v = ch[u][s[i] - 'a'];
            if(!v) v=init(sz+1);
            u = v;
        }
        end[u] = id;
    }

    // 構建AC自動機
    void build()
    {
        queue<int> q;
        for(int v:ch[0]) if(v)
            fail[v] = 0, q.push(v);
        while(!q.empty())
        {
            int u = q.front(); q.pop();
            for(int i = 0; i < 26; i++)
            {
                int &v = ch[u][i];
                if(v) 
                {
                    q.push(v);
                    fail[v] = ch[fail[u]][i];
                    last[v] = end[fail[v]] ? fail[v] : last[fail[v]]; //後綴鏈接
                }
                else v = ch[fail[u]][i]; //建立trie圖
            }
        }
    }
    
    // 查詢一個文本串中各終結節點出現了幾次, 保存在ans中
    void query(const char *s)
    {
        int now = 0;
        for(int i = 0; s[i]; i++)
        {
            now = ch[now][s[i] - 'a'];
            int id = end[now] ? now : last[now];
            while(id) 
            {
            	if(dp[i+1-pat[end[id]].size()]) 
            	{
            		tr[i+1] = end[id];
            		dp[i+1] = 1;
            		break;
            	}
            	id = last[id];
            }
        }
    }
}AC;

char tex[M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	memset(tr, -1, sizeof(tr));

	int n = read(); //文本串長度
	scanf("%s", tex);
	int ps = read();
	for(int i=1; i<=ps; ++i)
	{
		cin >> pat[i];
		string tst = pat[i];
		for(auto &c:tst) c = tolower(c);
		reverse(tst.begin(), tst.end());
		AC.insert(tst.c_str(), i);
	}
	dp[0] = 1;
	AC.build(); AC.query(tex);
	// for(int i=0; i<=n; ++i)
	// 	printf("i=%d dp=%d tr=%d\n",i,dp[i],tr[i] );
	stack<int> trs;
	for(int i=n; i; i-=pat[tr[i]].size())
	{
		//printf("i=%d tr=%d\n",i,tr[i] );
		trs.push(tr[i]);
	}
	while(trs.size())
	{
		cout << pat[trs.top()] << " ";
		trs.pop();
	}

    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章