题目链接: 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; }