AC自动机

Posted water-radish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AC自动机相关的知识,希望对你有一定的参考价值。

//AC自动机 
//相当于在Trie树上求KMP 
//可以求一个字符串的多个匹配子串 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
struct uio{
    int son[27],end,fail;//fail指针相当于KMP中的next[]数组 
}trie[1000001];
int n,cnt;
char c[1000001];
void insert(char* a)
{
    int now=0,len=strlen(a);
    for(int i=0;i<len;i++)
    {
        int c=int(a[i]-a);
        if(!trie[now].son[c])
            trie[now].son[c]=++cnt;
        now=trie[now].son[c];
    }
    trie[now].end+=1;
}
void get_fail()//求fail指针 
{
    queue<int> que;
    while(!que.empty()) que.pop();
    //将根节点的fail指针预处理出来
    //原因是根节点的fail指针指向了根节点自身
    //若在BFS中处理会导致其儿子也将fail指针指向儿子自身 
    for(int i=0;i<26;i++)
        if(trie[0].son[i])
        {
            trie[trie[0].son[i]].fail=0;
            que.push(trie[0].son[i]);
        }
    while(!que.empty())//BFS 
    {
        int now=que.front();
        que.pop();
        //在取出节点后,正确的操作应为: 
        //循环26个字母,若不存在儿子则continue 
        //否则查询:表示字母sth的儿子(下文称sth)的父亲(也就是刚从队列中取出的节点,下文称sth_fa), 
        //其fail指针指向的节点(下文称sth2_fa)是否也有一个表示字母sth的儿子(下文称sth2) 
        //若有则直接将sth的fail指针指向sth2 
        //否则查询sth2_fa的fail指针指向的节点是否也有一个表示字母sth的儿子 
        //重复以上过程直至找到一个符合条件的儿子或是回溯到root节点 
        //对于后者直接将sth的fail指针指向root即可 
        for(int i=0;i<26;i++)
        {
            if(trie[now].son[i])
            {
                trie[trie[now].son[i]].fail=trie[trie[now].fail].son[i];
                que.push(trie[now].son[i]);
            }
            else trie[now].son[i]=trie[trie[now].fail].son[i];
        }
        //那么以上代码为什么正确呢 
        //考虑分3种情况 
        //1.有此儿子且第一次查询就找到应指向的节点 则直接指向即可 
        //2.有此儿子但第一次查询找到了一个空节点 则直接指向即可 
        //原因是在遇到空节点时会将它的fail指针指向
        //其父亲的fail指针指向的节点的表示相同字母的儿子 
        //这就相当于做了本应在递归过程中进行的操作 
        //3.无此儿子 这种情况的操作不再赘述 
    }
}
void query(char* a)
{
    int now=0,len=strlen(a);
    int ans=0;
    for(int i=0;i<len;i++)
    {
        now=trie[now].son[a[i]-a];
        for(int j=now;j&&trie[j].end!=-1;j=trie[j].fail)
        //未回溯至根节点且未查询过 
            ans+=trie[j].end,trie[j].end=-1;
    }
    printf("%d
",ans);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%s",c),insert(c);
    trie[0].fail=0;//标志结束 
    get_fail();
    scanf("%s",c);
    query(c);
    return 0;
}

 

以上是关于AC自动机的主要内容,如果未能解决你的问题,请参考以下文章

POJ3691DNA repair(AC自动机,DP)

HDU4057 Rescue the Rabbit(AC自动机+状压DP)

Codeforces 86C Genetic engineering(AC自动机+DP)

POJ1699 Best Sequence(AC自动机+状压DP)

POJ - 2778 ~ HDU - 2243 AC自动机+矩阵快速幂

HDU2457 DNA repair(AC自动机+DP)