蒟蒻林荫小复习——AC自动机

AC自动机:Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。

今天蒟蒻林荫来复习AC自动机

前置芝士

  1. KMP算法
  2. TRIE树(这个不会的话出门右转幼儿园)

好吧我承认AC自动机要比KMP好理解。

如何建立一个AC自动机

  1. 建立一颗字典树,其中插入操作和普通字典树的一模一样
  2. 在字典树上构造fair数组(这也就是AC自动机与KMP联系起来的地方)
  3. 开始查找

如何构造一个fair数组?/fair数组是个啥?

fair数组与KMP中的next一样都是作为失配数组存在的,而对于节点X,fair所存的元素是fair[x的父亲]的一个与X相同的儿子,如果没有fair指向root。

也就是说fair[x]=某个与x节点相同的点或者root,并且fair[x]所在的链一定存在于X所在的这条链上,换句话说,fair[x]所跳转到的字符串一定是X所在串从X向前的一部分,且一定包括X。

至于如何求fair,就通过BFS来实现

下面来张图

  1. 红色线:root出队,将h和s两个点fair置为root,并将h和s入队
  2. 蓝色线:h出队,子节点e的fair等于自己父亲的fair的子节点中e的位置,不存在所以为root,e入队,同时s出队,子节点a的fair为自己父亲的fair的子节点中a的位置不存在为root,a入队,子节点h的fair为自己父亲fair的子节点中h的位置,存在,fair[tire[now][i]]=tire[fair[now]][i];h入队
  3. 绿色线:e出队,子节点r的fair等于自己父亲的fair(root)中子节点中r的位置,不存在为root,r入队,a出队,子节点y的fair同样为root,y入队,h出队,子节点e的fair等于父亲的fair的子节点中e的位置,fair[tire[now][i]]=tire[fair[now]][i],与左子树的e相连,e入队,子节点r的fair为root,入队;

这样的话fair数组就整理好了,对于不存在的点,tire[now][i]=tire[fair[now]][i]。如果这个点不存在,那么就指向自己父亲的fair的对应位置。类似于一个状态压缩,如果当前x的fair中并没有与x相同的元素,那么就前往fair[x]的fair,因为fair[x]代表的元素==x,fair[fair[x]]代表的元素==fair[x]代表的元素

void getfail()
{
    queue<int>q;
    for(int i=0;i<26;i++)
    {
        if(trie[0][i])
        {
            q.push(trie[0][i]);
            fail[trie[0][i]]=0;
        }
    }
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            if(trie[now][i])
            {
                fail[trie[now][i]]=trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else
            {
                trie[now][i]=trie[fail[now]][i];
            }
        }
    }
}
FAIR

 

万恶的fair终于整理完成了

下面来说说查询

AC自动机是用来查询原串中出现模式串次数的,所以这里引入一个变量tdword[x]代表在x节点结尾的模式串的个数

那么查询的第一重循环一定是在tire上确定原串每一位的位置的,联想到刚才fair的定义,也就是说一个点如果目前与原串匹配,那么这个点的fair及fair以上字符构成的串也一定能和原串匹配,所以说每到达一个节点,都要访问这个点可以通过fair数组所访问到的每一个点

 

int query(string x)
{
    int ans=0;
    int now=0;
    for(int i=0;i<x.size();i++)
    {
        now=trie[now][x[i]-'a'];for(int j=now;j&&tdword[j]!=-1;j=fail[j])
        {
            ans+=tdword[j]; 
            tdword[j]=-1;
        }
    }
    return ans;
}

 

而本人的代码对应的是洛谷上AC自动机的模板,该题要求统计出现模式串的种类数,第二重循环即为根据某个点跳fair,如果tdword[j]==-1的话就代表这个点已经在之前被访问过一遍,那么它的fair肯定也全部都跳完了。所以说跳到tdword[j]==-1的情况就停下来。

OK,完结撒花!

posted @ 2019-08-15 00:04  HA-SY林荫  阅读(192)  评论(0编辑  收藏  举报