CODEVS 2542單詞__fail樹
2542 單詞
2013年省隊選拔賽天津市隊選拔賽
時間限制: 2 s 空間限制: 256000 KB 題目等級 : 大師 Master 題目描述 Description小張最近在忙畢業,所以一直在讀論文。一篇論文是由許多單詞組成的。
但小張發現一個單詞會在論文中出現很多次,他想知道每個單詞分別在論文中出現了多少次。
輸入描述 Input Description
第一行一個整數N,表示有N個單詞,接下來N行,每行一個單詞,每個單詞都由小寫字母組成(N<=200)
輸出描述 Output Description輸出N個整數,第i行的數表示第i個單詞在文章中出現了多少次。
3
a
aa
aaa
6
3
1
數據範圍及提示 Data Size & Hint30%的數據 單詞總長度不超過10^3
100%的數據 單詞總長度不超過10^6
————————————————————————————————————————————————————————————————————————————————
一看是AC自動機是沒有問題的,但是建完AC自動機後該如何做呢?
當然可以直接用AC自動機的fail指針向前跳,但是這樣要枚舉單詞內的每個位置,嚴重超時。
這裏要用到一個很有用的工具——fail樹。
當AC自動機建完以後,我們發現每一個節點都只有一個失敗指針(根節點的可以忽略),且沿著失敗指針最終能走到根節點。
這樣我們將失敗指針反向就可以變成一棵樹。
這棵樹的有些重要的性質:
1、以x節點為根的子樹中每個節點代表的字符串都以x點所代表的字符串為後綴。
2、節點x代表的字符串的最大後綴為父節點代表的字符串。
這樣我們在插入字符串是把經過的每個節點的val值都加1,就相當於統計的對應前綴出現的次數。
然後通過dfs將val變為val加它所有子樹的val和。也就是以(s)為前綴的字符串個數+以(*+s)為前綴的字符串的個數+以(**+s)為前綴的個數+。。。。。
這樣s出現的次數就是s串尾對應的val值。
比較難想,但是fail樹真的很有用。
————————————————————————————————————————————————————————————————————————————————
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+3; 4 const int maxs=26; 5 struct edge{ 6 int u,v,next; 7 }e[maxn]; 8 int head[maxn],js=0; 9 void addage(int u,int v) 10 { 11 e[++js].u=u;e[js].v=v; 12 e[js].next=head[u];head[u]=js; 13 } 14 struct AC{ 15 int ch[maxn][maxs]; 16 int val[maxn],f[maxn]; 17 int sz; 18 AC(){ 19 memset(ch[0],0,sizeof(ch[0])); 20 val[0]=0; 21 sz=1; 22 } 23 int idx(char c){ 24 return c-‘a‘; 25 } 26 int insert(char *s){ 27 int n=strlen(s),cur=0; 28 for(int i=0,c;i<n;i++){ 29 c=idx(s[i]); 30 if(!ch[cur][c]){ 31 memset(ch[sz],0,sizeof(sz)); 32 val[sz]=0; 33 ch[cur][c]=sz++; 34 } 35 cur=ch[cur][c]; 36 val[cur]++; 37 } 38 return cur; 39 } 40 void getfail(){ 41 queue<int>q; 42 f[0]=0; 43 for(int c=0;c<maxs;c++){ 44 int v=ch[0][c]; 45 if(v){ 46 f[v]=0; 47 q.push(v); 48 addage(0,v); 49 } 50 } 51 while(!q.empty()){ 52 int u=q.front();q.pop(); 53 for(int c=0;c<maxs;++c){ 54 int v=ch[u][c]; 55 if(v){ 56 q.push(v); 57 int r=f[u]; 58 while(r && !ch[r][c])r=f[r]; 59 f[v]=ch[r][c]; 60 addage(f[v],v); 61 } 62 } 63 } 64 } 65 }ac; 66 int n; 67 int wz[201]; 68 char s[maxn]; 69 void dfs(int u,int f) 70 { 71 for(int i=head[u];i;i=e[i].next){ 72 int u=e[i].u,v=e[i].v; 73 if(v==f)continue; 74 dfs(v,u); 75 ac.val[u]+=ac.val[v]; 76 } 77 } 78 int main() 79 { 80 scanf("%d",&n); 81 for(int i=0;i<n;++i){ 82 scanf("%s",s); 83 wz[i]=ac.insert(s); 84 } 85 ac.getfail(); 86 dfs(0,-1); 87 for(int i=0;i<n;i++) 88 printf("%d\n",ac.val[wz[i]]); 89 return 0; 90 }View Code
CODEVS 2542單詞__fail樹