AC自动机
AC自动机--树上KMP
在博主还没懂其精髓并有足够语言组织能力之前,先挂着,以免把别人带入坑中。
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int
using namespace std;
struct su{
int fail; //匹配失败后跳到哪一个节点
int son[26]; //儿子的地址
int end; //有多少个模式串在这个节点结束
}ac[1000001];
int n,top;
string s;
inline int qr(){ char ch;
while((ch=getchar())<'0'||ch>'9');
int res=ch^48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+(ch^48);
return res;
}
inline void build(){
int l=s.length(),now=0,*son;
for(rg i=0;i<l;++i){
son=&ac[now].son[s[i]-'a']; //开个指针方便快捷
if(!*son)*son=++top; //没有这个字母的位置就新开一个
now=*son; //把下一位字母放进去
}++ac[now].end; //这个模式串在这个节点结束
}
inline void failed(){
queue<int> q; //用BFS来给树上KMP做预处理
for(rg i=0;i<26;++i)
if(ac[0].son[i])
ac[ac[0].son[i]].fail=0,//初始化
q.push(ac[0].son[i]); //根节点的子节点都指向根节点
rg i,j,*son,next;
while(!q.empty()){
i=q.front(); q.pop();
for(j=0;j<26;++j){
son=&ac[i].son[j]; //当前的节点
next=ac[ac[i].fail].son[j]; //当前节点匹配失败时转向的下一个节点
if(*son){
ac[*son].fail=next; //对应68,69行构成(局部)的KMP #1!
q.push(*son); //预备下一层
}else *son=next; //对应第67行,共同构成一个(整体)的KMP #2!
}
}
}
inline int find(){
int l=s.length(),now=0,ans=0;
for(rg i=0;i<l;++i){
now=ac[now].son[s[i]-'a']; //匹配下一层,对应59行
for(rg j=now;j&&ac[j].end>-1;j=ac[j].fail) //原理模仿的KMP算法
ans+=ac[j].end,ac[j].end=-1; //and改成-1:每个字符串最多匹配一次
}return ans;
}
int main(){ n=qr();
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
for(rg i=1;i<=n;++i)
cin>>s,build();//把模式串加入AC自动机
failed(); cin>>s;
printf("%d",find());//直接输出
return 0;
}