P3966 [TJOI2013]單詞
阿新 • • 發佈:2018-09-10
build fail using lin trie .org 優化 ref data
P3966 [TJOI2013]單詞
AC自動機
題意:輸入若幹個單詞,對於每個單詞,計算它在所有單詞中出現的次數(原題講的什麽鬼)。
attention:有重復單詞,還很多
顯然,我們可以用AC自動機來搞
但是對每個單詞都需要跑一遍過於費時。
於是我們想到用一個主串把這些單詞拼在一起,中間用特殊符號隔開。這樣我們就只要在主串上跑一遍AC自動機就可以了
但是這樣還不夠(90pts)
於是我們就要對AC自動機進行優化(以後都這樣寫吧qwq)。
引入一個last指針,保存某節點沿fail指針向前跳最近一個有結尾標記的節點,詢問時直接按last指針代替fail指針跳轉。搞定
對於重復單詞:用一個pre數組存儲某編號對應單詞的結尾節點
end.
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; struct data{ int nxt[26],end,fail,last; }a[1000002]; int n,cnt,pre[202]; long long f[202]; string q,g; inline void Trie_build(int id){ cin>>q; g=g+"#"+q; //用string方便地加上各個單詞int u=0,len=q.size(); for(int i=0;i<len;++i){ int p=q[i]-‘a‘; if(!a[u].nxt[p]) a[u].nxt[p]=++cnt; u=a[u].nxt[p]; } if(!a[u].end) a[u].end=id; pre[id]=a[u].end; //重復單詞處理 } inline void AC_build(){ queue <int> h; for(int i=0;i<26;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]); while(!h.empty()){ int x=h.front(); h.pop(); for(int i=0;i<26;++i){ int &to=a[x].nxt[i]; if(to){ a[to].fail=a[a[x].fail].nxt[i]; a[to].last= a[a[to].fail].end ? a[to].fail:a[a[to].fail].last; //last指針優化 h.push(to); }else to=a[a[x].fail].nxt[i]; } } } inline void AC_query(){ int u=0,len=g.size(); for(int i=0;i<len;++i){ if(g[i]==‘#‘) {u=0; continue;} u=a[u].nxt[g[i]-‘a‘]; for(int j=u;j;j=a[j].last) ++f[a[j].end]; //按last指針跳轉 } for(int i=1;i<=n;++i) printf("%lld\n",f[pre[i]]); //輸出的是 指向的節點 的編號 的值 } int main(){ //freopen("P3966.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;++i) Trie_build(i); AC_build(); AC_query(); return 0; }
P3966 [TJOI2013]單詞