計蒜客 蒜廠工作手冊 (ac自動機)
阿新 • • 發佈:2019-01-04
還是不很理解ac自動機的內涵,所以這道題卡住了,看了題解後恍然大悟。
因為是字典樹匹配,接著用兩個棧,一個存字串,另外一個存節點。
這有什麼用呢?原因是當找到一個單詞的時候,就可以模擬刪除這個單詞,也就是從棧頂取出這個單詞的所有字母跟節點序號。那麼現在節點棧最頂端就是刪除的這個單詞的前一個字母的節點標號,再從這進行下一輪的匹配,當走完所有的字母就會發現字母棧中儲存的正好就是刪除剩下來的字母。
#include <iostream> #include<cstring> using namespace std; const int MAX_N = 1e5+10; const int MAX_C = 26; int a; struct AC_Automaton{ int ch[MAX_N][MAX_C],fail[MAX_N],cnt[MAX_N];// ch 和 cnt 陣列與 Trie 樹中的一樣;fail 儲存的是失敗指標。ch 和 fail 預設都為 -1 int tot; // Trie 樹的總結點(不含根結點)個數 int top; int stk[MAX_N]; void init(){ memset(ch,-1,sizeof(ch)); memset(fail,0,sizeof(fail)); tot=top=0; memset(cnt,0,sizeof(cnt)); } void insert(char *str){ int p=0; for(int i=0;str[i];i++){ if(ch[p][str[i]-'a']==-1){ ch[p][str[i]-'a']=++tot; } p=ch[p][str[i]-'a']; } cnt[p]=strlen(str); } void build(){ int l=0,r=0,Q[MAX_N]; for(int i=0;i<MAX_C;i++){ if(ch[0][i]==-1){ ch[0][i]=0; }else{ Q[r++]=ch[0][i]; } } while(l<r){ int p=Q[l++]; for(int i=0;i<MAX_C;i++){ if(ch[p][i]==-1){ ch[p][i]=ch[fail[p]][i]; }else{ fail[ch[p][i]]=ch[fail[p]][i]; Q[r++]=ch[p][i]; } } } } int count(char *str){ int p=0; for(int i=0;str[i];i++){ p=ch[p][str[i]-'a']; str[a++]=str[i]; stk[top++]=p; if(cnt[p]){ a-=cnt[p]; top-=cnt[p]; p=top?stk[top-1]:0; } } } }ac; char s[100006]; char ss[100006]; int main() { int n; ac.init(); scanf("%s",ss); scanf("%d",&n); while(n--){ scanf("%s",s); ac.insert(s); } ac.build(); ac.count(ss); ss[a]='\0'; printf("%s\n",ss); return 0; }