zoukankan      html  css  js  c++  java
  • AC自动机

    AC自动机,全称Aho-Corasick自动机。如果没记错的话好像就是前缀自动机。

    其实AC自动机就是KMP上树的产物。理解了KMP,那AC自动机应该也是很好理解的。

    与KMP类似,AC自动机也是扔一个字符走一步。当前状态始终只有一个,每次如何走都是确定的,换句话说AC自动机是一种确定型有限状态自动机(DFA)。

    进行模式匹配是AC自动机的基本应用。如果稍加拓展一下,就可以知道在AC自动机上走k步就相当于产生了一个长为k、只包含给定字符集的字符串。借助这个性质,可以在AC自动机上DP来解决一些字符串有关问题。

    注意一般应用的时候都会把节点不存在的儿子指向跳fail之后的结果,这样就可以在匹配时省掉fail,代码简洁的同时还能提速。

    来点例题:

    1. COGS1913 AC自动机

    一道AC自动机模板题,没啥好说的。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=10010;
     6 void insert(const char*,int);
     7 void getfail();
     8 void match();
     9 void inc(int);
    10 int n,ch[maxn][26]={{0}},val[maxn]={0},fail[maxn]={0},last[maxn]={0},q[maxn],head=0,tail=0,cnt=0,a[110];
    11 char s[110][60],c;
    12 int main(){
    13 #define MINE
    14 #ifdef MINE
    15     freopen("ACautomata.in","r",stdin);
    16     freopen("ACautomata.out","w",stdout);
    17 #endif
    18     scanf("%d",&n);
    19     for(int i=1;i<=n;i++){
    20         scanf("%s",s[i]);
    21         insert(s[i],i);
    22     }
    23     getfail();
    24     match();
    25     for(int i=1;i<=n;i++)printf("%s %d
    ",s[i],a[i]);
    26 #ifndef MINE
    27     printf("
    -------------------------DONE-------------------------
    ");
    28     for(;;);
    29 #endif
    30     return 0;
    31 }
    32 void insert(const char *c,int i){
    33     int x=0;
    34     while(*c){
    35         if(!ch[x][*c-'a'])ch[x][*c-'a']=++cnt;
    36         x=ch[x][*c-'a'];
    37         c++;
    38     }
    39     val[x]=i;
    40 }
    41 void getfail(){
    42     int x,y;
    43     for(int c=0;c<26;c++)if(ch[0][c])q[tail++]=ch[0][c];
    44     while(head!=tail){
    45         x=q[head++];
    46         for(int c=0;c<26;c++){
    47             if(ch[x][c]){
    48                 q[tail++]=ch[x][c];
    49                 y=fail[x];
    50                 while(y&&!ch[y][c])y=fail[y];
    51                 fail[ch[x][c]]=ch[y][c];
    52                 last[ch[x][c]]=val[fail[ch[x][c]]]?fail[ch[x][c]]:last[fail[ch[x][c]]];
    53             }
    54             else ch[x][c]=ch[fail[x]][c];
    55         }
    56     }
    57 }
    58 void match(){
    59     char c;
    60     int x=0;
    61     for(;;){
    62         do c=getchar();while((c<'a'||c>'z')&&c!=EOF);
    63         if(c==EOF)return;
    64         x=ch[x][c-'a'];
    65         if(val[x])inc(x);
    66         else if(last[x])inc(last[x]);
    67     }
    68 }
    69 void inc(int x){
    70     if(!x)return;
    71     a[val[x]]++;
    72     inc(last[x]);
    73 }
    View Code

    2. POI2000 病毒

    对模式串建AC自动机,问题就转化为了在AC自动机上是否存在一个不经过任何危险节点(单词节点或者存在last的节点)且无限长的路径。换句话说,就是问AC自动机上有没有环。

    然后直接上dfs找环即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=30010;
    void insert(const char*,int=0);
    void init();
    void dfs(int);
    int n,ch[maxn][2]={{0}},val[maxn]={0},fail[maxn]={0},last[maxn]={0},q[maxn],cnt=0,head=0,tail=0;
    bool vis[maxn]={false},ins[maxn]={false},ok=false;
    char c[maxn];
    int main(){
    #define MINE
    #ifdef MINE
        freopen("wir.in","r",stdin);
        freopen("wir.out","w",stdout);
    #endif
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%s",c);
            insert(c);
        }
        init();
        dfs(0);
        if(!n||ok)printf("TAK");
        else printf("NIE");
    #ifndef MINE
        printf("
    -------------------------DONE-------------------------
    ");
        for(;;);
    #endif
        return 0;
    }
    void insert(const char* c,int x){
        while(*c){
            if(!ch[x][*c-'0'])ch[x][*c-'0']=++cnt;
            x=ch[x][*c-'0'];
            c++;
        }
        val[x]++;
    }
    void init(){
        int x,y;
        for(int c=0;c<2;c++)if(ch[0][c])q[tail++]=ch[0][c];
        while(head!=tail){
            x=q[head++];
            for(int c=0;c<2;c++){
                if(ch[x][c]){
                    q[tail++]=ch[x][c];
                    y=fail[x];
                    while(y&&!ch[y][c])y=fail[y];
                    fail[ch[x][c]]=ch[y][c];
                    last[ch[x][c]]=val[ch[y][c]]?ch[y][c]:last[ch[y][c]];
                }
                else ch[x][c]=ch[fail[x]][c];
            }
        }
    }
    void dfs(int x){
        if(val[x]||last[x])return;
        if(ins[x]){
            ok=true;
            return;
        }
        if(vis[x])return;
        vis[x]=ins[x]=true;
        for(int c=0;c<2;c++){
            if(ch[x][c]){
                dfs(ch[x][c]);
                if(ok)return;
            }
        }
        ins[x]=false;
    }
    View Code

    3. COGS2248 情书

    问你是否n个模式串都在文本串中出现过,AC自动机模板题。

    话说string瞎搞居然比AC自动机快……

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=100010;
     6 void insert(const char*,int);
     7 void getfail();
     8 void match(const char*);
     9 void inc(int);
    10 int n,ch[maxn][26]={{0}},fail[maxn]={0},last[maxn]={0},val[maxn]={0},q[maxn],head=0,tail=0,cnt=0,a[110];
    11 char s[1350010],c;
    12 bool ok;
    13 int main(){
    14 #define MINE
    15 #ifdef MINE
    16     freopen("lettera.in","r",stdin);
    17     freopen("lettera.out","w",stdout);
    18 #endif
    19     scanf("%d",&n);
    20     for(int i=1;i<=n;i++){
    21         scanf("%*[^a-z]");
    22         scanf("%[a-z]",s);
    23         insert(s,i);
    24     }
    25     getfail();
    26     for(;;){
    27         scanf("%*[^a-z]");
    28         if(scanf("%[a-z]",s)!=1)break;
    29         fill_n(a+1,n,0);
    30         match(s);
    31         ok=true;
    32         for(int i=1;i<=n;i++)if(!a[i]){
    33             ok=false;
    34             break;
    35         }
    36         printf(ok?"Yes
    ":"No
    ");
    37     }
    38 #ifndef MINE
    39     printf("
    -------------------------DONE-------------------------
    ");
    40     for(;;);
    41 #endif
    42     return 0;
    43 }
    44 void insert(const char *c,int i){
    45     int x=0;
    46     while(*c){
    47         if(!ch[x][*c-'a'])ch[x][*c-'a']=++cnt;
    48         x=ch[x][*c-'a'];
    49         c++;
    50     }
    51     val[x]=i;
    52 }
    53 void getfail(){
    54     int x,y;
    55     for(int c=0;c<26;c++)if(ch[0][c])q[tail++]=ch[0][c];
    56     while(head!=tail){
    57         x=q[head++];
    58         for(int c=0;c<26;c++){
    59             if(ch[x][c]){
    60                 q[tail++]=ch[x][c];
    61                 y=fail[x];
    62                 while(y&&!ch[y][c])y=fail[y];
    63                 fail[ch[x][c]]=ch[y][c];
    64                 last[ch[x][c]]=val[fail[ch[x][c]]]?fail[ch[x][c]]:last[fail[ch[x][c]]];
    65             }
    66             else ch[x][c]=ch[fail[x]][c];
    67         }
    68     }
    69 }
    70 void match(const char *c){
    71     int x=0;
    72     while(*c&&*c!='$'){
    73         x=ch[x][*c-'a'];
    74         if(val[x])inc(x);
    75         else if(last[x])inc(last[x]);
    76         c++;
    77     }
    78 }
    79 void inc(int x){
    80     if(!x)return;
    81     a[val[x]]++;
    82     inc(last[x]);
    83 }
    View Code

    其实例题还有很多,只不过限于时间(zi ji tai cai)没做,在此贴上大概思路:

    1. [SCOI 2012] 喵星球上的点名

    大概思路肯定是建AC自动机然后模式匹配。但是有一个问题,模式串太多,暴力匹配肯定会T。

    考虑每个节点的last,显然它们构成了一棵树。因此可以对last树做树上差分,最后一遍dfs求出每个模式串出现的次数。至于每次有多少模式串出现,直接用节点的深度就行了。

    2. 文本生成器(08年俞华程论文题)

    感觉并不用减法原理就可以写……(不过不一定正确,如果有发现不对的话请告诉我)

    先建AC自动机,然后就是一个AC自动机上的DP。

    令f[i][j][0/1]为从i出发再走k步的方案数,第三维0表示没有出现过模式串,1表示出现过了。

    转移方程:

    f[i][j][0]=sum{f[ch[i][c]][j-1][0]}

    f[i][j][1]=sum{f[ch[i][c]][j-1][1]+f[ch[i][c]][j-1][0]}

    边界:

    f[0][0][0]=1,其余j=0情况均为0。

    然后,显然这是一个线性递推式,而状态又不多,因此可以用矩阵快速幂优化。构造一个转移矩阵,然后乘乘乘就好了。

     

    尽头和开端,总有一个在等你。

  • 相关阅读:
    移除TDE
    SQL Server 聚集索引和非聚集索引的区别
    Serivce Broker 简单实用
    SQL Server 2012 ColumnStore索引测试
    UISegmentedControl的所有操作总结
    iPhone开发之深入浅出 — ARC之前世今生(三)
    什么是 ARC?ios5,xcode 4.2
    Present ViewController详解
    UITextField的总结
    iPhone开发资料之内存管理 ,循环引用导致的内存问题
  • 原文地址:https://www.cnblogs.com/hzoier/p/6024088.html
Copyright © 2011-2022 走看看