不会kmp和Trie树的请点击右上角X。
AC自动机与kmp的唯一区别便是从单模式串变成了多模式串。
那么与kmp相同,AC自动机中的fail指针是指向当前状态的最长后缀。
当然这个后缀要在Trie树中存在,且不能指向自己。当然在Trie树中不存在它的后缀时,fail指针指向根。
于是在用文本串匹配时,只要在走到每个状态时沿着此状态对应文本串当前字符的字符边向下走即可。
在文本串的某个字符失配时,需要移动到其fail指针指向的状态并继续尝试匹配此字符。
/* 这是一道模板题的核心代码 给出一个文本串和一个字典,输出在文本串中出现了字典中的多少个单词 */ void insert(){ int cur=0,len=strlen(str); for(int i=0;i<len;i++){ if(ch[cur][str[i]-'a']==0) ch[cur][str[i]-'a']=++size; cur=ch[cur][str[i]-'a']; } tag[cur]++; return ; } /* 将字符串插入Trie树并记录tag */ void get_fail(){ queue<int>Q; for(int i=0;i<26;i++) if(ch[0][i])Q.push(ch[0][i]); while(!Q.empty()){ int u=Q.front(); Q.pop(); for(int i=0;i<26;i++){ if(ch[u][i]){ Q.push(ch[u][i]); fail[ch[u][i]]=ch[fail[u]][i]; } else ch[u][i]=ch[fail[u]][i]; } } return ; } /* 求fail指针 这里有一个小技巧,网上许多AC自动机的代码在求fail指针时都需要用到一个while循环 但实际上我们可以将fail指针直接当做此状态的一个儿子,则可以省略掉一个循环(意会一下) */ int find(){ int u=0,cnt=0,len=strlen(str); for(int i=0;i<len;i++){ u=ch[u][str[i]-'a']; int tmp=u; while(tmp!=0&&!vis[tmp]) cnt+=tag[tmp],vis[tmp]=1,tmp=fail[tmp]; } return cnt; } /* 在自动机上跑文本串 注意一个细节,当你匹配到一个状态时,你也同时匹配到了此状态fail指针指向的状态 (因为那个状态表示的字符串是这个状态的字符串的后缀)所以在计数时也应该计算上它 注意标记不要重复记录 */
关于AC自动机其实还有很多种用途,在此不再赘述。
但大部分都是运用fail指针和树的性质进行匹配和dp等,需要选手结合具体题目灵活处理。