题目链接: hdu 2222
题目大意: 给出N个模式串,最后给出主串
求模式串在主串的出现过
解题思路: AC自动机的模版题,关于AC自动机:
AC自动机是多模式串匹配的算法,时间复杂度为O(n*m)
算法的实现结合了KMP和字典树的思想,其中难点在于理解失败指针
先把需要匹配的字符串建成字典树,然后再根据字典树建立失败指针
定义1:从距离根节点K/2的结点到a结点的字符串为S1
定义2:从根节点到b结点的字符串为S2
若S1==S2则a的失败指针指向b,若找不到与S1相等的字符串则a的失败指针指向root
如图:
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 1000010
struct snode{
int w,fail;
int next[26];
}Tree[250000];
int Index,list[10000];
char ch[MAX],ch1[100];
void Insert(int Star,int Tlen,int k) //建立字典树
{
int i,S=Star,child;
for(i=1;i<=Tlen;i++)
{
child=ch1[i-1]-'a';
if(Tree[S].next[child]==-1)
{
Tree[S].next[child]=Index++;
Tree[Index-1].w=0;
if(i==Tlen)
Tree[Index-1].w=1;
S=Tree[S].next[child];
}
else
{
if(i==Tlen)
Tree[Tree[S].next[child]].w++;
S=Tree[S].next[child];
}
}
}
void Buill_fail(int Star) //建立失败指针
{
int i,e,s,now,now_fail;
s=e=0;
Tree[0].fail=-1;
list[s++]=Star;
while(e!=s)
{
now=list[e++];
for(i=0;i<26;i++)
{
if(Tree[now].next[i]!=-1) // ** -1
{
now_fail=Tree[now].fail;
while(now_fail!=-1)
{
if(Tree[now_fail].next[i]!=-1) //改变孩子的失败指针 ** -1
{
Tree[Tree[now].next[i]].fail=Tree[now_fail].next[i];
break;
}
now_fail=Tree[now_fail].fail;
}
if(now_fail==-1) //不存在则把它孩子的失败指针指向根节点 ** -1
{
Tree[Tree[now].next[i]].fail=0;
}
list[s++]=Tree[now].next[i];
}
}
}
}
void DFS(int Star)
{
int i;
for(i=0;i<26;i++)
{
if(Tree[Star].next[i]!=-1)
{
DFS(Tree[Star].next[i]);
}
}
return ;
}
int Ac_auto(int Tlen)
{
int i,p=0,temp,child,sum=0;
for(i=1;i<=Tlen;i++)
{
child=ch[i-1]-'a';
while(Tree[p].next[child]==-1&&p!=0) //寻找失败指针 **00
{
p=Tree[p].fail;
}
p=(Tree[p].next[child]==-1)?0:Tree[p].next[child];
temp=p;
while(temp!=0&&Tree[temp].w!=-1) //匹配过的则下次不在匹配
{
sum+=Tree[temp].w;
Tree[temp].w=-1;
temp=Tree[temp].fail;
}
}
return sum;
}
int main()
{
int i,t,n;
scanf("%d",&t);
while(t--)
{
Index=1;
memset(Tree,-1,sizeof(Tree));
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",ch1);
Insert(0,strlen(ch1),i); //建立字典树
}
Buill_fail(0); //建立失败指针
DFS(0); //匹配
scanf("%s",ch);
printf("%d\n",Ac_auto(strlen(ch)));
}
return 0;
}