强势图解AC自动机

sshwy

2018-08-13 11:20:29

算法·理论

前置技能

简介

看dalao们AC自动机的Blog,大多数奆奆都会感性地说: AC_automation = KMP+TRIE<!--more--> 然而在作者重蹈覆辙辗转反侧n次后才明白,这东西说了等于没说。

建立AC自动机

建立一个AC自动机通常需要两个步骤:

然后就可以利用它进行多模式匹配了。

TRIE构建

构造失配(fail)指针

在讲构造以前,先来对比一下这里的fail指针与KMP中的next指针:

下面介绍构建fail指针的基础思想:(强调!基础思想!基础!)

下面放一张GIF帮助大家理解: 对字典树{i,he,his,she,hers}构建fail指针:

另外,在构建fail指针的同时,我们也对TRIE中模式串的结尾构建fail指针。这样在匹配到结尾后能自动跳转到下一个匹配项。具体见代码实现。

从代码深入剖析

框架

const int N=1000;
struct AC_automaton{
    int tr[N][26],cnt;//TRIE
    int e[N];//标记这个结点是不是字符串结尾
    int fail[N];//fail指针

    void insert(char * s){}//插入模式串
    void build(){}//构建fail指针
    int query(char *t){}//匹配函数
};
AC_automation ac;
}

字典树与字典图

这里的字典树根节点为0,我们将根节点的子节点一一入队。

然后开始BFS:

这三个Questions构成了fail指针构建的精髓。

Q1

Q2&Q3

这就是build完成的两件事:构建fail指针和建立字典图。这个字典图也会在查询的时候起到关键作用。

多模式匹配

Answer to Q

总结

到此,你已经理解了整个AC自动机的内容。我们一句话总结AC自动机的运行原理: 构建字典图实现自动跳转,构建失配指针实现多模式匹配。

代码

const int N=1000;
struct AC_automaton{
    int tr[N][26],cnt;//TRIE
    int e[N];//标记字符串结尾
    int fail[N];//fail指针

    void insert(char * s){//插入模式串
        int p=0;
        for(int i=0;s[i];i++){
            int k=s[i]-'a';
            if(!tr[p][k])tr[p][k]=++cnt;
            p=tr[p][k];
        }
        e[p]++;
    }
    void build(){
        queue<int>q;
        memset(fail,0,sizeof(fail));
        for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);
            //首字符入队
            //不直接将0入队是为了避免指向自己
        while(!q.empty()){
            int k=q.front();q.pop();//当前结点
            for(int i=0;i<26;i++){
                if(tr[k][i]){
                    fail[tr[k][i]]=tr[fail[k]][i];//构建当前的fail指针
                    q.push(tr[k][i]);//入队
                }
                else tr[k][i]=tr[fail[k]][i];
                    //匹配到空字符,则索引到父节点fail指针对应的字符,以供后续指针的构建
                    //类似并差集的路径压缩,把不存在的tr[k][i]全部指向tr[fail[k]][i]
                    //这句话在后面匹配主串的时候也能帮助跳转
            }
        }
    }
    int query(char *t){
        int p=0,res=0;
        for(int i=0;t[i];i++){
            p=tr[p][t[i]-'a'];
            for(int j=p;j&&~e[j];j=fail[j])res+=e[j],e[j]=-1;
        }
        return res;
    }
};
AC_automation ac;