秉着能偷懶就偷懶的精神,關於AC自動機本來不想看的,但是HanLp的源碼中用戶自定義詞典的識別是用的AC自動機實現的。唉~沒辦法,還是看看吧
AC自動機理論
Aho Corasick自動機,簡稱AC自動機,要學會AC自動機,我們必須知道什么是Trie,也就是字典樹。Trie樹,又稱單詞查找樹或鍵樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計和排序大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。之前已經寫過關於Tire與雙數組Tire(Double Array Tire,以下簡稱DAT)的文章。
AC自動機的優點
AC的優點其實包括了所有Tire樹的優點,而且更加強大,考慮下面的問題
對一個長字符串S,給定一個模式串T,查看模式串T是否在S中出現過?
這個問題簡單的算法就是便利S與T中的字符逐個比較,不一致則移到S的下一個字符。這種算法最壞情況下的時間復雜度是O(len(S) * len(T))
而廣為人知的KMP算法就是省去了逐個比較的過程,而是直接按照next數組中的內容來進行查找,復雜度為O(len(S) + len(T))
而AC自動機較之於DAT的優點就是增加一個fail表,而省去在DAT多模式匹配時無所謂的回溯,(不過后來HanLp的作者做了實驗,效率不如DAT呢。)
AC自動機的構造
知道了AC的強大,下面來看AC的構造,構造之前,先看一張AC的圖,對字典{FG,HE,HERS,HIS,SHE}構造的Tire樹就如下圖所示:


乍一看這個圖很復雜,其實,只是在TIRE的基礎上,對每個節點,加上了一個fail指針,如圖中的虛線所示
AC自動機的構造
AC建立是構建在TIRE基礎上的,DAT的兩要素為BASE與CHECK表,而AC的三要素為goto表,fail表與output表
goto: 即分支與兒子節點
fail :某狀態匹配失敗,回到fail所指狀態繼續匹配
output表: 狀態對應的輸出
把大象裝冰箱分3步,所以AC構造過程也分3步:
1. 對字典建立TIRE樹結構,建立過程中 goto表也隨之建好(即指向子節點的指針),output表初步建成,之后還要根據fail指針去擴充output,下圖"[]"中的內容即為初步的output表,對應於字典中的下標

2. 步驟1生成的TIRE樹,雖然有樹形結構,但狀態值都為0,下面用TIRE構造DAT,並且對狀態State賦值。
注意,構造過程中,對是一個詞的節點,即圖中的綠色節點,在構造DAT時,會長生一個子節點,即標識一個詞的結尾,類似與上一篇中提到的葉節點結構,到增加的子節點的轉移字符取0即可,新增節點的state值即為父節點的base值

3. 遍歷帶有狀態的TIRE,構造fail表與output表。
構造失敗指針的原理: 對某個節點,其產生於字母C,沿着此節點的父節點的失敗指針走,知道某個節點,他的分支狀態中也有字母C,然后把當前節點的失敗指針指向那個分支C指向的兒子節點,如果一直啊到root都沒有這樣的節點,則失敗指針指向root即可。原理同KMP算法,不明白可以谷歌之。構造完后的圖即如第一張圖所示。
下圖即構造完成后,各個表中的內容:

至此 AC自動機便構造完成了,接下來就看看怎么用它去分詞了。
參考資料
