一、基礎概念介紹
DFA全稱為:Deterministic Finite Automaton,即確定有窮自動機。其特征為:有一個有限狀態集合和一些從一個狀態通向另一個狀態的邊,每條邊上標記有一個符號,其中一個狀態是初態,某些狀態是終態。但不同於不確定的有限自動機,DFA中不會有從同一狀態出發的兩條邊標志有相同的符號。
NFA全稱為:Non-Deeterministic Finite State Automata,即不確定的有窮自動機: 對一個輸入符號,有兩種或兩種以上可能對狀態,所以是不確定的。
二、Trie tree算法
Trie tree,又稱字典樹、單詞查找樹,是一種樹形結構,用於保存大量的字符串,是DFA算法中最常見的一種。它的優點是:利用字符串的公共前綴來節約存儲空間。
將abject、ablaze、able、abound、about、accent、accept、best、bestow、bet……生成Trie tree圖,如下所示:
Trie tree類定義如下:
1 public class Trietree 2 { 3 public bool End; //當前節點是否有匹配到的字符串 4 public List<string> Results; // 匹配到的字符串集合 5 public Dictionary<char, Trietree > Nodes; //子節點 6 }
三、AC自動機算法
AC自動機是在trie tree基礎上進行優化的算法。即在tried tree的類上增加一個failure指針,如果當前點匹配失敗,則將指針轉移到failure指針指向的地方,這樣就不用回溯,而可以路匹配下去了。
例如使用上文生成的tried tree進行匹配【abestp】,
第一次,從a開始匹配,從左到右只能匹配到了ab。
第二次,從b開始匹配,從左到右匹配到了best,並且成功匹配了。
在這個案例中,出現了算法回溯,如第一次匹配失敗后,第二次還是從根節點匹配開始查找下一個節點,這就是回溯。
AC自動機增加failure指針后,在第一次匹配失敗時,就通過failure指針來到了第二次的b節點,減少回溯,從而優化性能。
AC自動機類定義如下:
1 public class ACTrietree 2 { 3 public bool End; //當前節點是否有匹配到的字符串 4 public List<string> Results; // 匹配到的字符串集合 5 public Dictionary<char, ACTrietree> Nodes; //子節點 6 public ACTrietree Failure; // Failure指針,匹配失敗后,使用此值進行下一次匹配 7 public ACTrietree Parent; //構造 Failure時 輔助用的 8 }
AC自動機構造方法太多了,此文不再闡述。
四、AC自動機的幾種改良
AC自動機算法在算法理論上是無法再進行優化的,但可以在代碼實現中優化:
1、Results集合,改用List<int>,做成索引集,就會減少內存使用量。
2、Nodes字典使用的是Dictionary<>類,也是內存使用大戶,並且Dictionary<>類查詢采用的是hash查找,改用自定義Dictionary類使用二分查找。
3、構建AC自動機比較費時,當關鍵字太多時,會有明顯感覺,可以使用序列化保存到文件,第二次加載文件就很快了。
4、查詢時,使用數組化查詢比使用類查詢快,常用的方法就是將ACTrietree結構轉成5組數組,分別是basePtr、nextPtr、check、failure、resultIndex五組。
后記:
敏感詞過濾是一個復雜工程,學會AC自動機算法,只能說已經達到敏感詞過濾的基礎。
不建議將Trietree轉成double array trie,原因很簡單,數組的長度是有最大限制的,如在C#代碼中定入下面代碼會報“Array dimensions exceeded supported range“
int[] c =new int[0x7fffffff];
將數組數量壓得越少,數組的最大長度越容易達到最大限制。