【AC自动机】洛谷三道模板题

 

【题目链接】

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\n",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 }
AC自动机1

 

【题目链接】

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\n",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 }
AC自动机2

 

【题目链接】

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 }
AC自动机3—差分做法

 

一种是拓扑排序的做法

  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\n",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 */
AC自动机3—拓扑排序

 

最后一种是最正宗的做法了,建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\n",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 */
AC自动机3—fail树

 

posted @ 2019-08-17 00:00  Osea  阅读(332)  评论(0编辑  收藏  举报