【题目链接】
https://www.luogu.org/problem/P3808
【题意】
给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
【题解】
不再介绍基础知识了,就是裸的模板题,直接贴上来。
【代码】
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N = 5e5+100; 7 const int M = 1e6+10; 8 queue < int > Q ; 9 typedef struct Aho_Corasick_Automaton{ 10 int Son[N][26],End[N],Fail[N],idx; 11 void Init(){ 12 idx = 0 ; 13 while( !Q.empty() ) Q.pop() ; 14 memset(Son , 0 , sizeof Son ); 15 memset(End , 0 , sizeof End ); 16 memset(Fail, 0 , sizeof Fail ); 17 } 18 void Insert(char s[]){ 19 int p = 0 ; 20 for(int i=0;s[i];i++){ 21 int t = s[i] - 'a'; 22 if( !Son[p][t] ) 23 Son[p][t] = ++idx; 24 p = Son[p][t] ; 25 } 26 End[p] ++ ; 27 } 28 29 void Build(){ 30 for(int i=0;i<26;i++){ 31 if( Son[0][i] ) 32 Fail[Son[0][i]] = 0 ,Q.push(Son[0][i]); 33 } 34 35 while( !Q.empty() ){ 36 int u = Q.front() ; 37 Q.pop(); 38 39 for(int i=0;i<26;i++){ 40 if( Son[u][i] ){ 41 Fail[Son[u][i]] = Son[Fail[u]][i] ; 42 Q.push( Son[u][i] ); 43 }else{ 44 Son[u][i] = Son[Fail[u]][i]; 45 } 46 } 47 } 48 } 49 50 void Query(char s[]){ 51 int p = 0,res = 0; 52 for(int i=0;s[i];i++){ 53 int t = s[i] - 'a'; 54 p = Son[p][t] ; 55 for(int j=p ; j && ~End[j] ; j = Fail[j] ){ 56 res += End[j] ; 57 End[j] = -1 ; 58 } 59 } 60 printf("%d ",res); 61 } 62 }AC_Machine ; 63 AC_Machine AC ; 64 char s[M]; 65 int n; 66 int main(){ 67 68 scanf("%d",&n); 69 //AC.Init(); 70 71 for(int i=0;i<n;i++){ 72 scanf("%s",s); 73 AC.Insert(s); 74 } 75 AC.Build(); 76 scanf("%s",s); 77 AC.Query(s); 78 return 0; 79 }
【题目链接】
https://www.luogu.org/problem/P3796
【题意】
有N个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串T中出现的次数最多。
和模板题1一样,就是最后结尾的标记加上一些小改动。
【代码】
1 // luogu-judger-enable-o2 2 #pragma GCC optimize(2) 3 4 #include<queue> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 using namespace std; 10 const int N = 5e5+1e4; 11 const int M = 1e6+10; 12 char Str[200][100],s[M]; 13 int Cnt[N],vis[N],n; 14 queue < int > Q ; 15 typedef struct Aho_Corasick_Automaton{ 16 int Son[N][26],End[N],Fail[N],idx; 17 void Init(){ 18 idx = 0 ; 19 while( !Q.empty() ) Q.pop() ; 20 memset(Cnt , 0 , sizeof Cnt ); 21 memset(vis , 0 , sizeof vis ); 22 memset(Son , 0 , sizeof Son ); 23 memset(End , 0 , sizeof End ); 24 memset(Fail, 0 , sizeof Fail ); 25 } 26 void Insert(char s[],int Id ){ 27 int p = 0 ; 28 for(int i=0;s[i];i++){ 29 int t = s[i] - 'a'; 30 if( !Son[p][t] ) 31 Son[p][t] = ++idx; 32 p = Son[p][t] ; 33 } 34 End[p] ++ ; 35 vis[p] = Id; 36 } 37 38 void Build(){ 39 for(int i=0;i<26;i++){ 40 if( Son[0][i] ) 41 Fail[Son[0][i]] = 0 ,Q.push(Son[0][i]); 42 } 43 44 while( !Q.empty() ){ 45 int u = Q.front() ; 46 Q.pop(); 47 48 for(int i=0;i<26;i++){ 49 if( Son[u][i] ){ 50 Fail[Son[u][i]] = Son[Fail[u]][i] ; 51 Q.push( Son[u][i] ); 52 }else{ 53 Son[u][i] = Son[Fail[u]][i]; 54 } 55 } 56 } 57 } 58 59 void Query(char s[]){ 60 int p = 0,res = 0; 61 for(int i=0;s[i];i++){ 62 int t = s[i] - 'a'; 63 p = Son[p][t] ; 64 for(int j=p ; j ; j = Fail[j] ){ 65 if( End[j] ){ 66 Cnt[vis[j]] ++ ; 67 } 68 } 69 } 70 for(int i=0;i<n;i++){ 71 res = max( Cnt[i] , res ) ; 72 } 73 cout << res << endl; 74 //printf("%d ",res); 75 for(int i=0;i<n;i++){ 76 if( res == Cnt[i] ){ 77 cout << Str[i] << endl; 78 } 79 } 80 } 81 }AC_Machine ; 82 AC_Machine AC ; 83 84 int main(){ 85 86 ios_base :: sync_with_stdio(false); 87 cin.tie(NULL) , cout.tie(NULL); 88 89 while ( cin >> n ,n ){ 90 AC.Init(); 91 for(int i=0;i<n;i++){ 92 //scanf("%s",s); 93 cin >> Str[i]; 94 AC.Insert(Str[i],i); 95 } 96 AC.Build(); 97 //scanf("%s",s); 98 cin >> s ; 99 AC.Query(s); 100 } 101 return 0; 102 }
【题目链接】
https://www.luogu.org/problem/P5357
【题意】
给你一个文本串 SS 和 nn 个模式串 T_{1..n},请你分别求出每个模式串 Ti 在 S 中出现的次数。
【题解】
这个题目就非常有意思了,
其实我还没怎么想就去洛谷看题解了,
发现是还是各显神通呀。大家都很厉害,我这里提供三个人的做法。
其实道理都是一样的。
一种是:进行类似于差分累加的过程。
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 const int N = 2e5+10; 8 const int M = 2e6+10; 9 typedef struct node{ 10 int son[26],fail; 11 int& operator [] (int x) {return son[x];} 12 }Node; 13 14 Node Trie[N]; 15 16 int Q[N],head[N],Next[N],Ans[N],d[N]; 17 int Head,Tail,n,idx; 18 char s[M]; 19 20 void Insert(char s[],int Id ){ 21 int p = 0 ; 22 for(int i=0;s[i];i++){ 23 int t = s[i] - 'a'; 24 if( !Trie[p][t] ) 25 Trie[p][t] = ++idx; 26 p = Trie[p][t] ; 27 } 28 Next[Id] = head[p] ; 29 head[p] = Id; 30 } 31 32 void Build(){ 33 Head = 1 , Tail = 0; 34 for(int i=0;i<26;i++) if( Trie[0][i] ) Q[++Tail] = Trie[0][i]; 35 while( Head <= Tail ){ 36 int u = Q[Head++] ; 37 for(int i=0;i<26;i++){ 38 if( Trie[u][i] ){ 39 Trie[Trie[u][i]].fail = Trie[Trie[u].fail][i] ; 40 Q[++Tail] = Trie[u][i] ; 41 }else{ 42 Trie[u][i] = Trie[Trie[u].fail][i]; 43 } 44 } 45 } 46 } 47 48 void Query(char s[]){ 49 int p = 0,res = 0; 50 for(int i=0;s[i];i++){ 51 int t = s[i] - 'a'; 52 p = Trie[p][t] ; 53 d[p] ++ ; 54 } 55 for(int i=idx;i;i--){ 56 for(int j=head[Q[i]] ; j ; j = Next[j] ) Ans[j] = d[Q[i]]; 57 d[ Trie[Q[i]].fail ] += d[ Q[i] ]; 58 } 59 for(int i=1;i<=n;i++){ 60 cout << Ans[i] << endl ; 61 } 62 } 63 64 int main(){ 65 66 ios_base :: sync_with_stdio(false); 67 cin.tie(NULL) , cout.tie(NULL); 68 69 cin >> n; 70 for(int i=1;i<=n;i++){ 71 cin >> s; 72 Insert(s,i); 73 } 74 Build(); 75 cin >> s ; 76 Query(s); 77 return 0; 78 }
一种是拓扑排序的做法
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 2e6+10; 4 typedef struct Node { 5 int son[26],Fail,End,Cnt; 6 void Clear(){ 7 memset(son,0,sizeof son ); 8 Fail = End = Cnt = 0 ; 9 } 10 int & operator [] (int x){ return son[x];} 11 }Node ; 12 Node Trie[N]; 13 char s[N]; 14 int Ans,idx=1,n,Head,Tail; 15 int Mp[N],vis[N],Ind[N],Q[N]; 16 17 void Insert( char s[] , int Id ){ 18 int p = 1 ; 19 for(int i=0;s[i];i++){ 20 int t = s[i] - 'a'; 21 if( !Trie[p][t] ) 22 Trie[p][t] = ++idx ; 23 p = Trie[p][t] ; 24 } 25 if( !Trie[p].End ) Trie[p].End = Id; 26 Mp[Id] = Trie[p].End 27 ; 28 } 29 30 void Build(){ 31 Head = 1 , Tail = 0 ; 32 for(int i=0;i<26;i++){ Trie[0][i] = 1 ; } 33 Q[++Tail] = 1 ; 34 while( Head <= Tail ){ 35 int u = Q[Head++] ; 36 for(int i=0;i<26;i++){ 37 int To = Trie[u][i] ; 38 if( To ){ 39 Trie[To].Fail = Trie[Trie[u].Fail][i]; 40 Ind[ Trie[To].Fail ] ++ ; 41 Q[++Tail] = Trie[u][i]; 42 }else{ 43 Trie[u][i] = Trie[Trie[u].Fail][i]; 44 } 45 } 46 } 47 } 48 void Topu( ){ 49 Head = 1 , Tail = 0 ; 50 for(int i=1;i<=idx;i++) if( !Ind[i] ) Q[++Tail] = i; 51 52 while( Head <= Tail ){ 53 int u = Q[Head++]; 54 vis[ Trie[u].End ] = Trie[u].Cnt ; 55 int To = Trie[u].Fail ; 56 Ind[To] -- ; 57 Trie[To].Cnt += Trie[u].Cnt; 58 if( Ind[To] == 0 ) 59 Q[++Tail] = To ; 60 } 61 } 62 void Query(char s[]){ 63 int p = 1 ; 64 for(int i=0;s[i];i++){ 65 p = Trie[p][s[i]-'a']; 66 Trie[p].Cnt ++ ; 67 } 68 } 69 70 int main() 71 { 72 scanf("%d",&n); 73 for(int i=1;i<=n;i++){ 74 scanf("%s",s); 75 Insert( s, i ); 76 } 77 Build() ; 78 scanf("%s",s); 79 Query(s); 80 Topu(); 81 for(int i=1;i<=n;i++){ 82 printf("%d ",vis[Mp[i]]); 83 } 84 } 85 86 /* 87 88 89 5 90 a 91 bb 92 aa 93 abaa 94 abaaa 95 abaaabaa 96 97 6 98 0 99 3 100 2 101 1 102 103 */
最后一种是最正宗的做法了,建fail树。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 2e6+10; 5 void dfs(int u); 6 void Add_edge(int u,int v); 7 8 char s[N]; 9 int head[N],nxt[N],to[N], cnt ; 10 11 int n,idx=1; 12 int Trie[N][26],fail[N],Match[N],Sz[N]; 13 14 int Q[N],Head,Tail; 15 16 void Insert(char s[] ,int Id){ 17 int p = 1 ; 18 for(int i=0;s[i];i++){ 19 int t = s[i]-'a'; 20 if( !Trie[p][t] ){ 21 Trie[p][t] = ++idx; 22 } 23 p = Trie[p][t] ; 24 } 25 Match[Id] = p ; 26 } 27 void Build(){ 28 Head = 1 , Tail = 0 ; 29 for(int i=0;i<26;i++) 30 Trie[0][i] = 1 ; 31 Q[++Tail] = 1 ; 32 while( Head <= Tail ){ 33 int u = Q[Head++] ; 34 for(int i=0;i<26;i++){ 35 if( Trie[u][i] ){ 36 fail[Trie[u][i]] = Trie[fail[u]][i]; 37 Q[++Tail] = Trie[u][i]; 38 }else{ 39 Trie[u][i] = Trie[fail[u]][i]; 40 } 41 } 42 } 43 } 44 void Query(char s[]){ 45 for(int p=1,i=0;s[i];i++){ 46 p = Trie[p][s[i]-'a']; 47 Sz[p] ++ ; 48 } 49 for(int i=2;i<=idx;i++){ 50 Add_edge( fail[i] , i ); 51 } 52 dfs( 1 ); 53 for(int i=1;i<=n;i++){ 54 printf("%d ",Sz[Match[i]]); 55 } 56 } 57 int main() 58 { 59 scanf("%d",&n); 60 for(int i=1;i<=n;i++){ 61 scanf("%s",s); 62 Insert(s,i); 63 } 64 Build(); 65 scanf("%s",s); 66 Query(s); 67 return 0; 68 } 69 void dfs(int u){ 70 for(int i=head[u];i;i=nxt[i]){ 71 int v = to[i] ; 72 dfs(v); 73 Sz[u] += Sz[v]; 74 } 75 } 76 void Add_edge(int u,int v){ 77 nxt[++cnt]= head[u]; 78 head[u] = cnt ; 79 to[cnt] = v ; 80 } 81 82 83 /* 84 85 5 86 a 87 bb 88 aa 89 abaa 90 abaaa 91 abaaabaa 92 93 94 6 95 0 96 3 97 2 98 1 99 100 */