1479:【例题1】Keywords Search
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 895 通过数: 254
【题目描述】
原题来自:HDU 2222
给定 n 个长度不超过 50 的由小写英文字母组成的单词准备查询,以及一篇长为 m 的文章,问:文中出现了多少个待查询的单词。多组数据。
【输入】
第一行一个整数 T,表示数据组数;
对于每组数据,第一行一个整数 n,接下去 n 行表示 n 个单词,最后一行输入一个字符串,表示文章。
【输出】
对于每组数据,输出一个数,表示文中出现了多少个待查询的单词。
【输入样例】
1
5
she
he
say
shr
her
yasherhs
【输出样例】
3
【提示】
数据范围:
对于全部数据,1≤n≤104,1≤m≤106 。
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int T,n,tree[N][26],tail[N],cnt,fail[N];//祖先
string s;
//AC自动机
void Insert()
{
int len=s.size(),now=0;
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
if(tree[now][x]==0)
tree[now][x]=++cnt;
now=tree[now][x];
}
tail[now]++;
}
void get_fail()//获取每个节点的祖先 bfs
{
//最长后缀的下标
queue<int> q;
for(int i=0;i<26;i++)
{
int x=tree[0][i];
if(x)//有儿子就帮他找fail
{
fail[x]=0;
q.push(x);
}
else//没有儿子就创造(同龄人认--收养)儿子:找同龄人
tree[0][i]=tree[fail[0]][i];
//else可省略
}
while(!q.empty())
{
int t=q.front();//这个父节点有无儿子
q.pop();
for(int i=0;i<26;i++)
{
int x=tree[t][i];
if(x)
{
q.push(x);
fail[x]=tree[fail[t]][i];
}
else
tree[t][i]=tree[fail[t]][i];
}
}
}
int Find()
{
int res=0,now=0,len=s.size();
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
now=tree[now][x];
//一次下,循环上
for(int j=now;j&&tail[j]!=-1;j=fail[j])//每个节点只能经过一次,不经过根节点
{
res+=tail[j];
tail[j]=-1;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while(T--)
{
memset(tree,0,sizeof(tree));
memset(tail,0,sizeof(tail));
memset(fail,0,sizeof(fail));
cnt=0;
cin >> n;
fail[0]=0;
while(n--)
{
cin >> s;
Insert();
}
get_fail();
cin >> s;
cout << Find() << endl;
}
return 0;
}