#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
struct node
{
int Next[500010][26];//数组存图
int fail[500010];//fail指针
int End[500010];//每个模式串出现的次数
int root,L;//根节点标号为0
int newnode()//新开节点
{
for(int i=0; i<26; i++)//初始化节点的对应字母为-1
Next[L][i]=-1;
End[L++]=0;//初始化模式串出现次数为0
return L-1;//返回当前节点的对应标号
}
void init()//初始化L和根节点
{
L=0;
root=newnode();
}
void Insert(char buf[])//插入模式串建立树//类似于字典树
{
int len=strlen(buf);
int now=root;
for(int i=0; i<len; i++)
{
if(Next[now][buf[i]-'a']==-1)//如果没有这个节点
Next[now][buf[i]-'a']=newnode();//开辟新节点
now=Next[now][buf[i]-'a'];//节点往下走
}
End[now]++;//完了之后该模式串对应的值加1
}
void build()//建立fail,将树转化为图
{
queue<int> q;
fail[root]=root;
for(int i=0; i<26; i++)
if(Next[root][i]==-1)
Next[root][i]=root;
else
{
fail[Next[root][i]]=root;//根下节点的fail都为root
q.push(Next[root][i]);
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0; i<26; i++)
if(Next[now][i]==-1)//如果该标号节点下不存在 i 字母则该标号节点指向标号匹配失败时节点下 i 字母的标号
Next[now][i]=Next[fail[now]][i];
else//如果存在该字母
{
fail[Next[now][i]]=Next[fail[now]][i];//该标号节点的失败指针指向该标号父节点失败标号下的 i 字母的标号
q.push(Next[now][i]);
}
}
}
int query(char buf[])//查询原串中出现过多少个模式串
{
int len=strlen(buf);
int now=root;
int res=0;
for(int i=0; i<len; i++)
{
now=Next[now][buf[i]-'a'];
int temp=now;
while(temp!=root)
{
res+=End[temp];//加上该字符串出现过几次
End[temp]=0;//防止重复加
temp=fail[temp];
}
}
return res;
}
// void debug()
// {
// for(int i=0; i<L; i++)
// {
// printf("id=%3d,fail=%3d,End=%3d,chi=[",i,fail[i],End[i]);
// for(int j=0; j<26; j++)
// printf("%2d",Next[i][j]);
// printf("]\n");
// }
// }
};
char buf[1000010];
node ac;
int main()
{
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
int n;
scanf("%d",&n);
ac.init();
for(int i=0; i<n; i++)//输入n个模式串
{
scanf("%s",buf);
ac.Insert(buf);
}
ac.build();
scanf("%s",buf);//输入原串
printf("%d\n",ac.query(buf));//输出原串中有多少个模式串
}
return 0;
}
AC自动机
最新推荐文章于 2022-12-18 11:31:42 发布