算法用途
一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。如果直接跑n遍KMP的话时间复杂度会较高(O(nm)),这时AC自动机算法便应运而生。
算法先决条件
要学会AC自动机,首先需要掌握Trie树和KMP算法。
算法思想
把所有的模板串存到Trie中,预处理出每个节点的失配指针(和KMP中的next数组很像),在查询时实现快速跳转。(其实就是在Trie上KMP)
算法要点
以洛谷P3808为例,完整代码请见模板1
构造Trie树
这个就是Trie树的插入操作,根本没有任何区别啊!
根据题目所求可以适当修改所需记录的量。
void nsrt(char s[]){
int node=0,len=strlen(s);//一定要先算好s的长度,不然会T掉的(别问我怎么知道的)
for (int i=0;i<len;i++){
int x=s[i]-'a';
if (!a[node][x])//如果没有这个儿子
a[node][x]=++k;//给这个儿子编号
node=a[node][x];//继续循环
}
f[node]++;//该字符串以这个字符为结尾
}
失配指针
在处理失配指针时,要使用队列扩展(想想为什么)。其他具体见代码注释
void asknxt(){
queue<int>que;//队列
//先把root的子节点加入队列中
for (int i=0;i<26;i++)
if (a[0][i]){
que.push(a[0][i]);
nxt[a[0][i]]=0;
}
while (!que.empty()){
int x=que.front();
que.pop();
int node=nxt[x];//node为x的失配指针所指向的节点
//枚举x的所有子节点
for (int i=0;i<26;i++)
if (a[x][i]){
//如果存在该节点