zoukankan      html  css  js  c++  java
  • 【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
    ",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
    ",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
    ",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
    ",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树
  • 相关阅读:
    Ubuntu
    SSH
    Markdown
    Mac OS 上的一些骚操作
    Linux Bash
    JConsole
    IDEA
    Groovy
    Github
    Git
  • 原文地址:https://www.cnblogs.com/Osea/p/11366943.html
Copyright © 2011-2022 走看看