AC自动机基本操作
(1) 在AC自动机中,我们首先将每一个模式串插入到Trie树中去,建立一棵Trie树,然后构建fail指针。
(2) fail指针,是穿插在Trie树中各个结点之间的指针,顾名思义,就是当匹配失败的时候,用于引导p指针回溯,就和KMP算法中的next数组道理相同。
#include<bits/stdc++.h> using namespace std; #define MAX 26 //字典树关键字为‘a’~‘b’ char str[1000005]; //主串(文章) int n; //模式串共有n串 //字典树结点定义 struct Node { Node*next[MAX]; Node*fail; int sum; }*qu[500005]; void init(Node*root) { for(int i=0;i<MAX;i++) root->next[i]=NULL; } //向字典树内添加模式串 void Insert(Node*root,char*ch) { Node*p=root; while(*ch) { int index=*ch-'a'; if(p->next[index]==NULL) { p->next[index]=(Node*)malloc(sizeof(Node)); init(p->next[index]); p->next[index]->sum=0; } p=p->next[index]; ch++; } p->sum++; } //利用模式串 字典树建树 Node*TrieCreate() { char ch[65]; Node*root=(Node*)malloc(sizeof(Node)); init(root); for(int i=0;i<n;i++) { scanf("%s",ch); Insert(root,ch); } return root; } //在字典树内构建 失配指针fail void BuildFail(Node*root) { int head=0,tail=0,i; root->fail=NULL; qu[tail++]=root; while(head<tail) { Node*t=qu[head++]; Node*p=NULL; for(i=0;i<26;i++) { if(t->next[i]) { if(t==root) t->next[i]->fail=root; else { p=t->fail; while(p) { if(p->next[i]) { t->next[i]->fail=p->next[i]; break; } p=p->fail; } if(!p) t->next[i]->fail=root; } qu[tail++]=t->next[i]; } } } } //AC自动机 ,返回主串中模式串的数量(文章中关键字的数量) int AC(Node*root,char*str) { int len=strlen(str),cnt=0; Node*p=root; while(*str) { while(p->next[*str-'a']==NULL&&p!=root)p=p->fail; p=p->next[*str-'a']; p=(p==NULL)?root:p; Node*t=p; while(t!=root&&t->sum!=-1) { cnt+=t->sum; t->sum=-1; t=t->fail; } str++; } return cnt; } int main() { cin>>n; Node*root=TrieCreate(); scanf("%s",str); BuildFail(root); printf("%d ",AC(root,str)); return 0; }