题目描述
给定 n 个长度不超过 50 的由小写英文字母组成的单词,以及一篇长为 m 的文章。
请问,有多少个单词在文章中出现了。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
对于每组数据,第一行一个整数 n,接下去 n 行表示 n 个单词,最后一行输入一个字符串,表示文章。
输出格式
对于每组数据,输出一个占一行的整数,表示有多少个单词在文章中出现。
数据范围
1≤n≤104,
1≤m≤106
输入样例
1
5
she
he
say
shr
her
yasherhs
输出样例
3
代码如下
这个题就是一个最基本的ac自动机的模板题
//ac自动机
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include<assert.h>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=1e6+5,M=5e5+5;
int n;
char s[N];
int tr[M][26],cnt[M],idx;
int ne[M],q[M];
void insert() //字典树的插入操作
{
int p=0;
for(int i=0;s[i];i++)
{
int t=s[i]-'a';
if(!tr[p][t]) tr[p][t]=++idx;
p=tr[p][t];
}
cnt[p]++;
}
void build() //建立ac自动机
{
int head=0,tail=-1;
for(int i=0;i<26;i++)
if(tr[0][i]) q[++tail]=tr[0][i];
while(tail>=head)
{
int t=q[head++];
for(int i=0;i<26;i++)
{
int p=tr[t][i];
if(!p) continue;
int j=ne[t];
while(j&&!tr[j][i]) j=ne[j];
if(tr[j][i]) j=tr[j][i];
ne[p]=j;
q[++tail]=p;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
idx=0;
memset(tr,0,sizeof tr);
memset(ne,0,sizeof ne);
memset(cnt,0,sizeof cnt);
scanf("%d",&n);
for(int i=0;i<n;i++) //建立字典树
{
scanf("%s",s);
insert();
}
build();
scanf("%s",s);
int ans=0;
for(int i=0,j=0;s[i];i++) //母串匹配
{
int t=s[i]-'a';
while(j&&!tr[j][t]) j=ne[j];
if(tr[j][t]) j=tr[j][t];
int p=j;
while(p)
{
ans+=cnt[p]; //如果cnt[p]>0,那么说明该位置匹配成功
cnt[p]=0;
p=ne[p];
}
}
printf("%d\n",ans);
}
return 0;
}
//tri图
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include<assert.h>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=1e6+5,M=5e5+5;
int n;
char s[N];
int tr[M][26],cnt[M],idx;
int ne[M],q[M];
void insert() //字典树的插入操作
{
int p=0;
for(int i=0;s[i];i++)
{
int t=s[i]-'a';
if(!tr[p][t]) tr[p][t]=++idx;
p=tr[p][t];
}
cnt[p]++;
}
void build() //建立tri图
{
int head=0,tail=-1;
for(int i=0;i<26;i++)
if(tr[0][i]) q[++tail]=tr[0][i];
while(tail>=head)
{
int t=q[head++];
for(int i=0;i<26;i++)
{ //tri图其实就是把ac自动机的匹配过程直接内嵌到了tr[][]数组的内部
int p=tr[t][i];
if(!p) tr[t][i]=tr[ne[t]][i];
else {
ne[p]=tr[ne[t]][i];
q[++tail]=p;
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
idx=0;
memset(tr,0,sizeof tr);
memset(ne,0,sizeof ne);
memset(cnt,0,sizeof cnt);
scanf("%d",&n);
for(int i=0;i<n;i++) //建立字典树
{
scanf("%s",s);
insert();
}
build();
scanf("%s",s);
int ans=0;
for(int i=0,j=0;s[i];i++) //母串匹配
{
int t=s[i]-'a';
j=tr[j][t];
int p=j;
while(p)
{
ans+=cnt[p]; //如果cnt[p]>0,说明匹配成功
cnt[p]=0;
p=ne[p];
}
}
printf("%d\n",ans);
}
return 0;
}