【题目链接】
【题意】
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
【题解】
与AC自动机模板题3类似,但是这个题目记录的子串是不能重叠的。
利用下标从后往前历遍,然后把对应的位置的地方叠加到公共后缀。原因是:如果该单词出现过,那么对应的公共后缀肯定也出现过。

1 #include<cstdio>
2 #include<algorithm>
3 using namespace std;
4 const int N = 1005;
5 const int M = 1e6+10;
6 char S[N][205];
7 int Trie[M][26],fail[M],End[M],Sz[M];
8 int Q[M],Head,Tail;
9 int head[M],nxt[M],to[M],cnt;
10 int n,idx=1;
11
12 void Add_edge(int u,int v);
13 void dfs(int u);
14 void Insert( char s[] , int Id){
15 int p = 1 ;
16 for(int i=0;s[i];i++){
17 int t = s[i] - 'a' ;
18 if( !Trie[p][t] )
19 Trie[p][t] = ++idx ;
20 p = Trie[p][t] ;
21 Sz[p] ++ ;
22 }
23 End[Id] = p ;
24 }
25 void Build(){
26 Head = 1 , Tail = 0;
27 for(int i=0;i<26;i++) Trie[0][i] = 1 ;
28
29 Q[++Tail] = 1 ;
30 while( Head <= Tail ){
31 int u = Q[Head++];
32 for(int i=0;i<26;i++){
33 int To = Trie[u][i];
34 if( To ){
35 fail[To] = Trie[fail[u]][i];
36 Q[++Tail] = To;
37 }else{
38 Trie[u][i] = Trie[fail[u]][i];
39 }
40 }
41 }
42 }
43 void Query(){
44
45 /*for(int i=1,p=1;i<=n;i++){
46 p = 1 ;
47 for(int j=0;S[i][j];j++){
48 int t = S[i][j] - 'a' ;
49 p = Trie[p][t];
50 Sz[p] ++;
51 }
52 }*/
53
54 for(int i=idx;i>=1;i--){
55 // Add_edge(fail[i],i);
56 Sz[fail[Q[i]]] += Sz[Q[i]];
57 }
58 //dfs(1);
59 for(int i=1;i<=n;i++){
60 printf("%d\n",Sz[End[i]]);
61 }
62 }
63 int main()
64 {
65 scanf("%d",&n);
66 for(int i=1;i<=n;i++){
67 scanf("%s",S[i]);
68 Insert(S[i],i);
69
70 //printf("### %s ### \n",S[i]);
71 }
72 Build();
73 //printf("@@@@\n");
74 Query();
75 return 0;
76 }
77 void dfs(int u){
78 for(int i=head[u];i;i=nxt[i] ){
79 int To = to[i] ;
80 dfs( To ) ;
81 Sz[u] += Sz[To] ;
82 }
83 }
84 void Add_edge(int u,int v){
85 nxt[++cnt] = head[u];
86 head[u] = cnt ;
87 to[cnt] = v ;
88 }
