AC自动机:Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。
今天蒟蒻林荫来复习AC自动机
前置芝士
好吧我承认AC自动机要比KMP好理解。
如何建立一个AC自动机
如何构造一个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来实现
下面来张图
这样的话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终于整理完成了
下面来说说查询
AC自动机是用来查询原串中出现模式串次数的,所以这里引入一个变量tdword[x]代表在x节点结尾的模式串的个数
那么查询的第一重循环一定是在tire上确定原串每一位的位置的,联想到刚才fair的定义,也就是说一个点如果目前与原串匹配,那么这个点的fair及fair以上字符构成的串也一定能和原串匹配,所以说每到达一个节点,都要访问这个点可以通过fair数组所访问到的每一个点
int query(string x) { int ans=0; int now=0; for(int i=0;i<s.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,完结撒花!
原文:https://www.cnblogs.com/XLINYIN/p/11355527.html