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

匿名 (未验证) 提交于 2019-12-02 23:54:01

【题目链接】

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

【题意】

SnT_{1..n}Ti

【题解】

这个题目就非常有意思了,

其实我还没怎么想就去洛谷看题解了,

发现是还是各显神通呀。大家都很厉害,我这里提供三个人的做法。

其实道理都是一样的。

一种是:进行类似于差分累加的过程。

 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树

来源: https://www.cnblogs.com/Osea/p/11366943.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!