http://acm.hdu.edu.cn/showproblem.php?pid=2222
题意:多模式串匹配,输出所有单词在字符串中出现的总次数。
AC自动机模板题,学习这位大佬的博客https://blog.csdn.net/creatorx/article/details/71100840,受益匪浅。
//https://blog.csdn.net/creatorx/article/details/71100840
//hdu 2222
#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=1000010;
struct node{
node *next[26];//子节点指针数组
node *fail;//失配指针
int sum;//这个节点是不是一个单词的结尾及相应个数
node()
{
for(int j=0;j<26;j++) next[j]=NULL;
sum=0;fail=NULL;
}
};
int cnt;
node* root;
node* newnode;
char key[70];
char pattern[maxn];
//建立字典树
void Insert(char *s)
{
node* p=root;
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(p->next[x]==NULL)
{
newnode=new node();
p->next[x]=newnode;
}
p=p->next[x];
}
p->sum++;
}
//求解每个节点的失配指针
//每个节点的失配指针指向的是以当前节点表示的字符为最后一个字符的最长当前字符串的后缀字符串的最后一个节点
void getfail()
{
queue<node*> q;
q.push(root);
node* p;
node* tmp;
while(!q.empty())
{
tmp=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(tmp->next[i])
{
if(tmp==root)
tmp->next[i]->fail=root;
else
{
p=tmp->fail;
while(p)
{
if(p->next[i])
{
tmp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL) tmp->next[i]->fail=root;
}
q.push(tmp->next[i]);
}
}
}
}
//匹配
void ac_automation(char *ch)
{
node* p=root;
int len=strlen(ch);
for(int i=0;i<len;i++)
{
int x=ch[i]-'a';
while(!p->next[x]&&p!=root) p=p->fail;
p=p->next[x];
if(!p) p=root;
node* tmp=p;
while(tmp!=root)
{
if(tmp->sum>=0)
{
cnt+=tmp->sum;
tmp->sum=-1;
}
else break;
tmp=tmp->fail;
}
}
}
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
root=new node();
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",key);
Insert(key);
}
scanf("%s",pattern);
cnt=0;
getfail();
ac_automation(pattern);
printf("%d\n",cnt);
}
return 0;
}