简介
AC自动机,全称Aho-Corasick自动机,适用于存在多个模板串的字符串匹配问题,如果没有AC自动机,你可能需要对n个模板串分别求一趟KMP,但是复杂度过高,而AC自动机可以一次匹配,效率更优秀。
实现
KMP是在字符串上线性匹配,而AC自动机则在字符串的集合上匹配,什么东西可以把一大堆字符串吧、放一起存储?Trie!所以AC自动机其实就是在Trie上生成KMP的失配函数。
对于一棵建好的Trie,接下来就是类似于KMP的失配函数的构造,这里要一层一层的构造,所以需要用BFS来遍历。其他大致相同,这里还有一点,当找到了某个单词后,可能找到其他的单词。例如找到his后有is,所以还要构造一个lst数组表示如果按照失配函数继续走时最近的一个单词编号,用于提高效率(蓝书上提到这个lst数组在正规文献内叫做后缀链接)。
模板
求模板串出现了几个(不能算重)
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=500005;
int n,len,tst,ch[maxn][26],w[maxn],lst[maxn],nxt[maxn],que[maxn];
char s[1000005];
struct AC{
void clear(int x){memset(ch[x],0,sizeof(ch[x])); w[x]=lst[x]=nxt[x]=0;}
void insist(){
int now=0,L=strlen(s);
for (int i=0;i<L;i++){
if (!ch[now][s[i]-'a']) {ch[now][s[i]-'a']=++len; clear(len);}
now=ch[now][s[i]-'a'];
}
w[now]++;
}
void makeF(){
int hed=0,til=0;
for (int i=0;i<26;i++)
if (ch[0][i]){
que[++til]=ch[0][i];
nxt[ch[0][i]]=lst[ch[0][i]]=0;
}
while (hed!=til){
int x=que[++hed];
for (int i=0;i<26;i++)
if (ch[x][i]){
int now=ch[x][i]; nxt[now]=ch[nxt[x]][i];
lst[now]=(w[nxt[now]])?nxt[now]:lst[nxt[now]];
que[++til]=now;
}else ch[x][i]=ch[nxt[x]][i]; //记忆化操作,提高效率
}
}
int get_num(int x){
int num=0;
for (;x;w[x]=0,x=lst[x]) num+=w[x]; //如果是求匹配个数就把w[x]=0去掉
return num;
}
int find(){
int now=0,num=0,L=strlen(s);
for (int i=0;i<L;i++){
now=ch[now][s[i]-'a'];
num+=get_num(now);
}
return num;
}
}tr;
void _work(){
scanf("%d",&n); len=0; tr.clear(0);
for (int i=1;i<=n;i++){scanf("%s",s); tr.insist();}
tr.makeF(); scanf("%s",s); printf("%d\n",tr.find());
}
int main()
{
freopen("search.in","r",stdin);
freopen("search.out","w",stdout);
scanf("%d",&tst);
while (tst--) _work();
return 0;
}