题面
【题目描述】
给定n个长度不超过50的小写字母组成的单词,以及一篇长为m的文章,问有多少个单词在文章出现。
输入
第一行一个整数T,表示测试数据组数。
对于每组测试数据,每一行一个整数n,接下去n行n个单词,最后一行输入一个字符串,表示文章。
【输出】
对于每组测试数据,输出一个数,表示有多少个单词在文章中出现。
【样例输入】
1
5
she
he
say
shr
her
yasherhs
【样例输出】
3
提示
n
<
=
1
0
4
,
m
<
=
1
0
6
n<=10^4,m<=10^6
n<=104,m<=106
算法分析
AC自动机模板
参考程序
写法一
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int trie[N][26],tot=1,vis[N],nxt[N];
int n,T;
char s[N];
queue<int> q;
void built()
{
int len=strlen(s);
int u=1; //根节点
for(int i=0;i<len;i++)
{
int v=s[i]-'a';
if(trie[u][v]==0) trie[u][v]=++tot;//根节点为1,tot初始化一定设为1
u=trie[u][v];
}
vis[u]++; //结束标记
}
void bfs()
{
for(int i=0;i<=25;i++) //添加虚拟结点0,所有边都指向根节点1 ,作用就是使得1的孩子结点指向1
trie[0][i]=1;
nxt[1]=0;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<=25;i++)
{
int v=trie[u][i];
if(v==0) trie[u][i]=trie[nxt[u]][i]; //优化,如果不存在则指向nxt[u]相同字符转移边,就不用再递归去找nxt[u]
else
{
q.push(v);
nxt[v]=trie[nxt[u]][i]; //存在,u的转移边结点v等于nxt[u]相同转移边的结点
}
}
}
}
int find()
{
int u=1,ans=0;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int v=s[i]-'a';
u=trie[u][v]; //优化后不需要判断是否存在结点u的转移边
for(int j=u;j&&vis[j]!=-1;j=nxt[j]) //统计数量
{
ans+=vis[j];
vis[j]=-1;
}
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
tot=1;
memset(vis,0,sizeof(vis));
memset(nxt,0,sizeof(nxt));
memset(trie,0,sizeof(trie));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
built();
}
bfs();
scanf("%s",s);
printf("%d\n",find());
}
return 0;
}
写法二
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int trie[N][26],tot=1,vis[N],nxt[N];
int n,T;
char s[N];
queue<int> q;
void built()
{
int len=strlen(s);
int u=1; //根节点
for(int i=0;i<len;i++)
{
int v=s[i]-'a';
if(trie[u][v]==0) trie[u][v]=++tot;//根节点为1,tot初始化一定设为1
u=trie[u][v];
}
vis[u]++; //结束标记
}
void bfs()
{
//初始化,根节点的孩子结点nxt指向根节点
for(int i=0;i<=25;i++)
{
int v=trie[1][i];
if(v) nxt[v]=1,q.push(v);
else trie[1][i]=1; //优化,不存在直接又从1开始
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<=25;i++)
{
int v=trie[u][i];
if(v==0) trie[u][i]=trie[nxt[u]][i]; //优化,如果不存在则指向nxt[u]相同字符转移边,就不用再递归去找nxt[u]
else
{
q.push(v);
nxt[v]=trie[nxt[u]][i]; //存在,u的转移边结点v等于nxt[u]相同转移边的结点
}
}
}
}
int find()
{
int u=1,ans=0;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int v=s[i]-'a';
u=trie[u][v]; //优化后不需要判断是否存在结点u的转移边
for(int j=u;j!=1&&vis[j]!=-1;j=nxt[j]) //统计数量
{
ans+=vis[j];
vis[j]=-1;
}
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
tot=1;
memset(vis,0,sizeof(vis));
memset(nxt,0,sizeof(nxt));
memset(trie,0,sizeof(trie));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
built();
}
bfs();
scanf("%s",s);
printf("%d\n",find());
}
return 0;
}