题目链接: hdu 3065
题目大意: 给出N个模式串,最后给出主串
问有模式串在主串中出现的次数
解题思路: AC自动机建立字典树的用w值标记第几个模式串
定义k值,匹配时若字典树中的某个结点不等于k且w不为0则记录该点
有多个主串需要匹配,所以不需要改变w的值,但可以判断k的值
若该失败指针的结点k值已经被匹配过则不需要再次匹配
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 52
struct snode{
int w,fail;
int next[26];
}Tree[MAX*1010];
int Index,visit[1010],listb[1000001];
char ch[2000010],ch1[1010][MAX];
void Insert(int Star,int Tlen,int k) //建立字典树
{
int i,S=Star,child;
for(i=1;i<=Tlen;i++)
{
child=ch1[k][i-1]-'A';
if(Tree[S].next[child]==-1)
{
if(i==Tlen)
Tree[Index].w=k;
Tree[S].next[child]=Index++;
}
else
{
if(i==Tlen)
Tree[Tree[S].next[child]].w=k;
}
S=Tree[S].next[child];
}
}
void Build_fail(int Star) //建立失败指针
{
int i,e,s,tempv,now_fail;
e=s=0;
listb[s++]=Star;
while(e!=s)
{
tempv=listb[e++];
for(i=0;i<26;i++)
{
if(Tree[tempv].next[i]!=-1)
{
now_fail=Tree[tempv].fail;
while(now_fail!=-1)
{
if(Tree[now_fail].next[i]!=-1)
{
Tree[Tree[tempv].next[i]].fail=Tree[now_fail].next[i];
break;
}
now_fail=Tree[now_fail].fail;
}
if(now_fail==-1)
Tree[Tree[tempv].next[i]].fail=0;
listb[s++]=Tree[tempv].next[i];
}
}
}
}
int Ac_auto(int Tlen) //BFS匹配
{
int i,p=0,sum=0,child,temp;
for(i=1;i<=Tlen;i++)
{
if(ch[i-1]<'A'||ch[i-1]>'Z') //可能出现其他字符,跳过不匹配
{
p=0;
continue;
}
child=ch[i-1]-'A';
while(p!=0&&Tree[p].next[child]==-1)
{
p=Tree[p].fail;
}
p=(Tree[p].next[child]==-1)?0:Tree[p].next[child];
temp=p;
while(temp!=0&&Tree[temp].w!=-1) //记录出现的次数
{
visit[Tree[temp].w]++;
sum++; //不需要擦除
temp=Tree[temp].fail;
}
}
return sum;
}
int main()
{
int n,i,k;
while(scanf("%d",&n)!=EOF)
{
Index=1;
memset(Tree,-1,sizeof(Tree));
memset(visit,0,sizeof(visit));
for(i=1;i<=n;i++)
{
scanf("%s",ch1[i]);
Insert(0,strlen(ch1[i]),i); //建立字典树
}
Build_fail(0); //建立失败指针
getchar();
gets(ch); //主串可能有空格
k=Ac_auto(strlen(ch)); //匹配
for(i=1;i<=n;i++)
{
if(visit[i])
{
printf("%s: %d\n",ch1[i],visit[i]);
}
}
}
return 0;
}