/*
洛谷 p3808
*/
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+1000;
struct tire
{
int c[N][26],val[N],fail[N],cnt;
//字典树
void Insert(char *s){
int len=strlen(s);int now=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!c[now][v]) c[now][v]=++cnt;
now=c[now][v];
}
val[now]++;
}
//构造fail指针
void build(){
queue<int>Q;
for(int i=0;i<26;i++){//第二层的fail指针提前处理一下
if(c[0][i]){
fail[c[0][i]]=0;//指向根节点
Q.push(c[0][i]);
}
}
while(!Q.empty()){//bfs求fail指针
int u=Q.front();
Q.pop();
for(int i=0;i<26;i++){//枚举所有子节点
if(c[u][i]){//存在这个子节点
fail[c[u][i]]=c[fail[u]][i];
//子节点的fail指针指向当前节点的
//fail指针所指向的节点的相同子节点
Q.push(c[u][i]);
}
//不存在这个子节点
else c[u][i]=c[fail[u]][i];
//当前节点的这个子节点指向当
//前节点fail指针的这个子节点
}
}
}
int query(char *s){//AC自动机匹配
int len=strlen(s),now=0,ans=0;
for(int i=0;i<len;i++){
now=c[now][s[i]-'a'];//向下一层
for(int t=now;t&&val[t]!=-1;t=fail[t]){//循环求解
ans+=val[t];
val[t]=-1;
}
}
return ans;
}
}ac;
int n;
char buf[N*10];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",buf);
ac.Insert(buf);
}
ac.build();
scanf("%s",buf);
printf("%d\n",ac.query(buf));
return 0;
}
这是一道AC自动机的裸题。
AC自动机(优化后可称Trie图)是一种(可以理解为被过分简化的)有限状态自动机。
所以可以简单的理解为将KMP放在Trie树上。
注意如果每次跳fail边复杂度过高,一次存储完可以进行优化。
这样的AC自动机就成为了Trie图。
不过这题的std是从香港新闻产业来的,加了一个跳转移边的剪枝优化。