(转)从Trie到AC自动机

Trie

Trie是一种好玩的数据结构.它的每个结点存的是字母,因此得名`字母树`.

出一张图让大家感受下.

(image powered by SaiBu NaoCu)

上面那是一棵插入了

ape,app,applicant,application,bake,ban,banana

等词的Trie.红色结点表示接受态.

显然,查找时只需顺着链照下来,插入只需边查找边插入.

(删除只需除去接受态,或此时在它没有子结点时删除到它的最近接受态父结点)

好懂好写高效率.

AC自动机

AC自动机是一种基于Trie的数据结构.它是一个真正的自动机.

AC自动机,简单地说就是加了一些奇怪东西的Trie.

(powered by cocoa....Cacoo)

实线表示Trie上的路径,虚线表示Fail指针.

Fail指针是什么呢?当你在这个结点上时,对于下一个字符失匹配时你要走的路.很类似于KMP的next数组.

它的定义也是基本一样的.最长有相同前缀的后缀(的那个前缀的最后一个字母结点的指针).

使用起来更是一样.计算也是一样的.顺着fat(s[p-1])的fail(next[k])跳到可以匹配为止.

简单吧?和KMP很像.

那么问题又来了.怎么输出呢?

我们再画个图想想.

原来,顺着Fail指针一直走下去即可啊...

那么我们就有了用AC自动机匹配的算法.

匹配第一个`A`,从root往下找

找到匹配,A的匹配加一.向下寻找下一个字符找到Null.

 

因Fail指针回跳到root.寻找字符C.

寻找字符B.不是接受态,继续.

A是一个接受态.沿着Fail指针走回去输出结果.

-------------------

匹配C失跳.

------------------------------

...........................

...................最终

结果

构造一个AC自动机

那么,如何构造一个AC自动机呢?

显然可以在每加入一个结点时沿着它的父亲的Fail指针走,走到第一个有相同字符子结点的找到那个子结点,将Fail指向那个结点;如果到root还没找到有同字符子结点,将Fail指向root.

这是在线的做法.离线当然可以用BFS解决,这样保证了当一个结点被处理时它的父亲那层的结点已经处理完.Fail结点最近也只可能在父亲那层.复杂度应与在线办法相同.

Update: 犯了个SB错,AC自动机不可在线.原因Fail指针可能改变.

图表地址:https://cacoo.com/diagrams/xqj6UFk5zcllgHGW

posted @ 2017-08-20 22:37  #Egoist#  阅读(172)  评论(0编辑  收藏  举报