【题目链接】
【题意】
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
【题解】
与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 ",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 ### ",S[i]); 71 } 72 Build(); 73 //printf("@@@@ "); 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 }