浅谈AC自动机
浅谈AC(WA)自动机(无指针)
看之前做好心理准备,写得很垃圾,大佬勿喷,本蒟蒻连省一都不是
身为蒟蒻的我在昨天学会了KMP以后终于理解了AC自动机的原理,这里在博客里写一下给同样看不懂而且迷茫的大佬们。
首先我们要明确一个道理,这个AC自动机不是那种用来AC每一种题目的一个bug,是一种专门用来处理字符串的模板。
学习AC自动机你首先要学会如何使用(WA,TLE,MLE,RE....自动机)看毛片算法(KMP)和Trie树(字典树)。
首先我们了解到KMP是用来解决一个模板串和一个文本串的匹配算法的。
例如:(模板串)abacavavadad, (文本串)aba 出现的次数
但是当我们要解决多个模板串在一个文本串中出现的次数的时候呢?先打多个KMP然后一个一个去匹配吗?这样时间复杂度很明显是显著上升的
例如:(模板串)sheicascasdmed, (文本串)she,cas,sca,sd.. 出现的次数
这时候我们要冷静,不能砸电脑,要膜大佬,这时候就要用到Aho-Corasick automaton。AC自动机算法分为3步:构造一棵Trie树,构造失败指针和模式匹配过程。
是不是很复杂,没错,我当时也很懵逼。所以这个图就放着看看不管了。
所以建议看一下一个关于AC自动机的非常有用的讲解(本蒟蒻不会讲解,一定要看)视频。
然后我们大概一个明白了,AC自动机就是第一步建立一个字典树,
void build() { int len=strlen(ss);//每一个模式串的长度,建立一个Trie树 int now=0; for(int i=0;i<len;i++) { if(tr[now].vis[ss[i]-'a']==0) tr[now].vis[ss[i]-'a']=++cnt ; now=tr[now].vis[ss[i]-'a']; } tr[now].end+=1;//统计单词数 }
第二步,通过KMP算法的fail指针,在Trie树上的一个单词结束后或无法匹配后指向另外一个单词的某个部分用来进行节时的查询(精髓所在)
void get() { queue<int>q; for(int i=0;i<26;i++) { if(tr[0].vis[i]!=0) { tr[tr[0].vis[i]].fail=0; q.push(tr[0].vis[i]); } } while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<26;i++) { if(tr[u].vis[i]!=0) { tr[tr[u].vis[i]].fail=tr[tr[u].fail].vis[i]; q.push(tr[u].vis[i]); } else tr[u].vis[i]=tr[tr[u].fail].vis[i]; } } }
第三步就是直接就把文本串放进去匹配了好吧
int query() { int len=strlen(ss); int now=0,ans=0; for(int i=0;i<len;i++) { now=tr[now].vis[ss[i]-'a']; for(int t=now;t&&tr[t].end!=-1;t=tr[t].fail) { ans+=tr[t].end; tr[t].end=-1; } } return ans; }
然后就结束了,AC自动机的基本功能就是解决多个模板串在一个文本串的情况。多做题目一定有利于理解的。