fail树就是将Trie图的Fail指针反指,从而生成一棵树,这个树的性质是:子节点对应字符串为以当前串为后缀,而子节点为原串的前缀,前缀的后缀就是嵌套在原串中的子串。
模板:BZOJ3172
Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
3
a
aa
aaa
a
aa
aaa
Sample Output
6
3
1
3
1
建完fail树统计子节点个数即可。
代码:
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 struct trnt{ 6 int ch[26]; 7 int fl; 8 int wgt; 9 int hd; 10 }tr[2000000]; 11 struct ent{ 12 int twd; 13 int lst; 14 }e[1000000]; 15 int fnd[1000]; 16 char tmp[2000000]; 17 int cnt; 18 int siz; 19 int n; 20 std::queue<int>Q; 21 void ade(int f,int t) 22 { 23 cnt++; 24 e[cnt].twd=t; 25 e[cnt].lst=tr[f].hd; 26 tr[f].hd=cnt; 27 } 28 void add(char *a,int x) 29 { 30 int root=0; 31 int len=strlen(a+1); 32 for(int i=1;i<=len;i++) 33 { 34 int c=a[i]-'a'; 35 if(!tr[root].ch[c]) 36 tr[root].ch[c]=++siz; 37 root=tr[root].ch[c]; 38 tr[root].wgt++; 39 } 40 fnd[x]=root; 41 return ; 42 } 43 void Build() 44 { 45 int root=0; 46 for(int i=0;i<26;i++) 47 if(tr[root].ch[i]) 48 Q.push(tr[root].ch[i]); 49 while(!Q.empty()) 50 { 51 root=Q.front(); 52 Q.pop(); 53 for(int i=0;i<26;i++) 54 { 55 if(tr[root].ch[i]) 56 { 57 tr[tr[root].ch[i]].fl=tr[tr[root].fl].ch[i]; 58 Q.push(tr[root].ch[i]); 59 }else 60 tr[root].ch[i]=tr[tr[root].fl].ch[i]; 61 } 62 } 63 /*--------------------------以上是Trie图-------------------*/ 64 65 66 67 /*--------------------------以下为fail树-------------------*/ 68 for(int i=1;i<=siz;i++) 69 ade(tr[i].fl,i); 70 return ; 71 } 72 void dfs(int x) 73 { 74 for(int i=tr[x].hd;i;i=e[i].lst) 75 { 76 int to=e[i].twd; 77 dfs(to); 78 tr[x].wgt+=tr[to].wgt; 79 } 80 } 81 int main() 82 { 83 scanf("%d",&n); 84 for(int i=1;i<=n;i++) 85 { 86 scanf("%s",tmp+1); 87 add(tmp,i); 88 } 89 Build(); 90 dfs(0); 91 for(int i=1;i<=n;i++) 92 printf("%d ",tr[fnd[i]].wgt); 93 return 0; 94 }