题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222
题意:求许多短串在一个长串里出现的个数
分析:原本想用hash求各个串的hash值,依次匹配,结果TLE了。似乎有一个hashtable可以解决,但我写的会爆内存,只能去学一学AC自动机了,刚好这是一道AC自动机的例题。最难的就是建立fail指针,和KMP的next指针差不多。
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int kind=26;
const int L=1000000+5;
char str[50+5],str2[L];
struct Node
{
int cnt;
Node *next[kind],*fail;
Node()//初始化节点
{
cnt=0;
memset(next,0,sizeof(next));
fail=NULL;
}
};
void ac_insert(char *str,Node *root)//插入同时创建Trie树
{
int i=0;
Node *tail=root;
while(str[i])//若不存在则new一个,否则tail往下走
{
int index=str[i]-'a';
if(tail->next[index]==NULL)
tail->next[index]=new Node;
tail=tail->next[index];
i++;
}
tail->cnt++;//短串的结尾标记,包括了多个相同短串的情况
}
void ac_build(Node *root)//建立fail指针
{
root->fail=NULL;
queue<Node*>q;
q.push(root);
while(!q.empty())
{
Node *tail=q.front();
q.pop();//出队
for(int index=0;index<kind;index++)//遍历
{
if(tail->next[index])
{
q.push(tail->next[index]);//入队
if(tail==root) //根下的第一层fail指针都指向root
tail->next[index]->fail=root;
else
{
Node *temp=tail->fail;
while(temp!=NULL)
{
if(temp->next[index])//找到树中相同的元素
{
tail->next[index]->fail=temp->next[index];
break;
}
temp=temp->fail;
}
if(temp==NULL)//没找到树中相同的元素
tail->next[index]->fail=root;
}
}
}
}
}
int ac_find(char *str,Node *root)//遍历长串
{
int i=0;
Node *tail=root;
int ans=0;
while(str[i])
{
int index=str[i]-'a';
while(tail->next[index]==NULL&&tail!=root)//若不匹配则tail走向fail直到匹配或者到root
tail=tail->fail;
if(tail->next[index])//若匹配上
{
tail=tail->next[index];
Node *temp=tail;
while(temp!=root)//把所有匹配上的cnt加上
{
ans+=temp->cnt;
temp->cnt=0;
temp=temp->fail;
}
}
i++;
}
return ans;
}
int main ()
{
//freopen("in.txt","r",stdin);
int cas;
scanf("%d",&cas);
while(cas--)
{
int n;
scanf("%d",&n);
Node *root=new Node;
for(int i=0;i<n;i++)
{
scanf("%s",str);
ac_insert(str,root);
}
ac_build(root);
scanf("%s",str2);
int ans=ac_find(str2,root);
printf("%d\n",ans);
}
return 0;
}