AC自动机,其实就是Trie树与KMP的结合,且有dfa(有限状态机)的性质.
理解的关键点:
1. fail指针 起到回溯作用
2.每次匹配都是主串不动,移动指针now去回溯找后缀的前缀
3.一个优化点,将NULL指向root 编码更简单.
考察时一般也会问道dfa的性质.
AC自动机解决问题:
1.多模式串匹配
2.给定一个长度,满足XX条件能构造多少个.
ABCD 多模式串匹配
EF dfa转移,求共有状态数,矩阵加速.
模板
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cstring> #include<queue> using namespace std; const int maxn=500010; const int inf=0x3f3f3f3f; const int chart=128; /** 构造Trie树需要的next[26] 指向下个节点(26)失败指针fail 到此是否为word end 根root 大小 sz 函数 init()初始化root与sz newNode() 清空next 设置end insert ac_build query **/ struct AC{ int next[maxn][chart]; int fail[maxn]; int end[maxn]; int id[maxn]; int yp[maxn]; int root,L; void init(){L=0;root=newNode();} int newNode(){ memset(next[L],-1,sizeof(next[L])); end[L]=0; return L++; } void insert(char*s,int type,int k){ int now=root; for(int i=0;s[i];i++){ if(next[now][s[i]]==-1) next[now][s[i]]=newNode(); now=next[now][s[i]]; } end[now]++; id[now]=k; yp[now]=type; } void build(){ queue<int> q; fail[root]=root; for(int i=0;i<chart;i++){ if(next[root][i]==-1) next[root][i]=root; else{ fail[next[root][i]]=root; q.push(next[root][i]); } }/** 这种写法将root下没有字符的next都指向自身 这样避免了query时的额外判断 将next[]没有指向的改为父亲失配指针指向的"同字符"并不改变AC自动机的特征 这样无非两种情况,(长度定然小于失配处的长度bfs) 一种它有就指向,另一种root **/ while(!q.empty()){ int now=q.front();q.pop(); for(int i=0;i<chart;i++){ if(next[now][i]==-1) next[now][i]=next[fail[now]][i];///这里要注意 else{ q.push(next[now][i]); fail[next[now][i]]=next[fail[now]][i]; } } } } void query(char*s){ int now=root; for(int i=0;s[i];i++){ now=next[now][s[i]]; int tmp=now; while(tmp!=root){ if(end[tmp]) { ///find word and do what you want } tmp=fail[tmp]; } } } }ac; int main(){ return 0; }
原文地址:http://blog.csdn.net/gg_gogoing/article/details/44408889