AC自动机

假设手里有N个串,和原文进行匹配,如果跑KMP,得把原文跑N次,实在是太费时间了。AC自动机借助于Trie的压缩功能,将多个串压成一个“树”,用这个树进行KMP,这就是贝尔实验室发明的AC自动机。
AC自动机主要有三个操作;insert,getfail,print,find。
insert的操作和Trie一样。
void Insert(string str,int v)
{
    int u=0,c;
    for(int i=0;i<str.size();i++)
    {
        c=str[i]-'a';
        if(!ch[u][c])
        {
            val[sz]=0;
            ch[u][c]=sz++;
        }
        u=ch[u][c];
    }
    val[u]=v;
}
insert的操作

getfail采用BFS,逐个检查,记录后缀链接last数组

void GetFail()
{
    queue<int> Q;
    f[0]=0;
    for(int c=0;c<26;c++)
    {
        int u=ch[0][c];
        if(u)
        {
            f[u]=last[u]=0;
            Q.push(u);
        }
    }
    while(!Q.empty())
    {
        int r=Q.front();Q.pop();
        for(int c=0;c<26;c++)
        {
            int u=ch[r][c];
            if(!u)
            {
                ch[r][c]=ch[f[r]][c];
                continue;
            }
            Q.push(u);
            int v=f[r];
            while(v&&!ch[v][c]) v=f[v];
            f[u]=ch[v][c];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
getfail的操作

find对应的是原文

void Find(string str)
{
    int j=0,c;
    for(int i=0;i<str.size();i++)
    {
        c=str[i]-'a';
        j=ch[j][c];
        if(val[j]) print(j);
        else if(last[j]) print(last[j]);
    }
}
find的操作

print表示在确定一个串后的操作

void print(int j)
{
    if(j)
    {
        cnt[val[j]]++;
        print(last[j]);
    }
}
print的操作

整个自动机中,最重要的是后缀链接last数组的使用,对于一个单词的尾结点,可能对应多个串,需要沿着往回走,看看有没有串了。

 

@练习题

HDU 2222

HDU 2896

HDU 3065


posted @ 2014-10-02 20:32  Physcal  阅读(264)  评论(0编辑  收藏  举报