题目链接:https://vjudge.net/problem/HDU-2222
题意:给出 t 组数据,每组 n 个单词,再给出一个字符串,求这 n 个单词在字符串中出现的次数
思路:字符串多模版匹配,AC 自动机模版题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e5+5;
const int maxm=1e6+5;
int tot;
int trie[maxn][26];
int val[maxn]; ///记录单词出现次数,标记字符串结尾
int fail[maxn]; ///失配指针
char q[maxm];
void insert(char w[]) ///基本插入
{
int p=1;
int len=strlen(w);
for(int i=0;i<len;i++)
{
int ch=w[i]-'a';
if(trie[p][ch]==0)
{
trie[p][ch]=++tot;
}
p=trie[p][ch];
}
val[p]++;
}
void get_fail() /// 处理回溯数组,用了一个虚拟的零号节点,下面两行表示它的所有儿子与一号建边
{
queue<int>q;
for(int i=0;i<26;i++)///*****
{
trie[0][i]=1;
}
while(!q.empty())
{
q.pop();
}
fail[1]=0;///一号节点回溯到零号****
q.push(1);///用到了零号节点,先压入 1 。如果不设零号的话,是压入 1 的所有儿子。两种,注意区别 */
while(!q.empty())
{
int up=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(trie[up][i])
{
fail[trie[up][i]]=trie[fail[up]][i];
q.push(trie[up][i]);
}
else
{
trie[up][i]=trie[fail[up]][i];
}
}
}
}
int query(char w[])
{
int ans=0;
int p=1;
int len=strlen(w);
for(int i=0;i<len;i++)
{
int ch=w[i]-'a';
p=trie[p][ch];
int j=p;
while(j>1&&val[j])
{
ans+=val[j];
val[j]=0;
j=fail[j];
}
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
tot=1;
memset(trie,0,sizeof(trie));
memset(fail,0,sizeof(fail));
memset(val,0,sizeof(val));
int n;
scanf("%d",&n);
while(n--)
{
char w[55];
scanf("%s",w);
insert(w);
}
get_fail();
scanf("%s",q);
int ans=query(q);
printf("%d\n",ans);
}
}