AC自動機

參考資料:AC自動機GIF動圖(來自油管)

以下文章節(jié)選自:王爭老師 AC自動機:如何用多模式串匹配實現(xiàn)敏感詞過濾功能浊仆?

AC 自動機實際上就是在 Trie 樹之上腮猖,加了類似 KMP 的 next 數(shù)組凡伊,只不過此處的 next 數(shù)組是構(gòu)建在樹上罷了糠馆。如果代碼表示橘茉,就是下面這個樣子:

public class AcNode {
  public char data; 
  public AcNode[] children = new AcNode[26]; // 字符集只包含 a~z 這 26 個字符
  public boolean isEndingChar = false; // 結(jié)尾字符為 true
  public int length = -1; // 當 isEndingChar=true 時,記錄模式串長度
  public AcNode fail; // 失敗指針
  public AcNode(char data) {
    this.data = data;
  }
}

所以朱庆,AC 自動機的構(gòu)建盛泡,包含兩個操作:

  • 將多個模式串構(gòu)建成 Trie 樹;
  • 在 Trie 樹上構(gòu)建失敗指針(相當于 KMP 中的失效函數(shù) next 數(shù)組)娱颊。

關(guān)于如何構(gòu)建 Trie 樹,請參考Trie樹的部分凯砍。所以箱硕,這里我們就重點看下,構(gòu)建好 Trie 樹之后悟衩,如何在它之上構(gòu)建失敗指針剧罩?
我用一個例子給你講解。這里有 4 個模式串座泳,分別是 c惠昔,bc,bcd挑势,abcd镇防;主串是 abcd。


Trie 樹中的每一個節(jié)點都有一個失敗指針潮饱,它的作用和構(gòu)建過程来氧,跟 KMP 算法中的 next 數(shù)組極其相似。所以要想看懂這節(jié)內(nèi)容,你要先理解 KMP 算法中 next 數(shù)組的構(gòu)建過程啦扬。如果你還有點不清楚中狂,建議你先回頭去弄懂 KMP 算法。

假設(shè)我們沿 Trie 樹走到 p 節(jié)點扑毡,也就是下圖中的紫色節(jié)點胃榕,那 p 的失敗指針就是從 root 走到紫色節(jié)點形成的字符串 abc,跟所有模式串前綴匹配的最長可匹配后綴子串瞄摊,就是箭頭指的 bc 模式串勤晚。

這里的最長可匹配后綴子串,我稍微解釋一下泉褐。字符串 abc 的后綴子串有兩個 bc赐写,c,我們拿它們與其他模式串匹配膜赃,如果某個后綴子串可以匹配某個模式串的前綴挺邀,那我們就把這個后綴子串叫作可匹配后子串

我們從可匹配后綴子串中跳座,找出最長的一個端铛,就是剛剛講到的最長可匹配后綴子串。我們將 p 節(jié)點的失敗指針指向那個最長匹配后綴子串對應(yīng)的模式串的前綴的最后一個節(jié)點疲眷,就是下圖中箭頭指向的節(jié)點禾蚕。



計算每個節(jié)點的失敗指針這個過程看起來有些復(fù)雜。其實狂丝,如果我們把樹中相同深度的節(jié)點放到同一層换淆,那么某個節(jié)點的失敗指針只有可能出現(xiàn)在它所在層的上一層。

我們可以像 KMP 算法那樣几颜,當我們要求某個節(jié)點的失敗指針的時候倍试,我們通過已經(jīng)求得的、深度更小的那些節(jié)點的失敗指針來推導(dǎo)蛋哭。也就是說县习,我們可以逐層依次來求解每個節(jié)點的失敗指針。所以谆趾,失敗指針的構(gòu)建過程躁愿,是一個按層遍歷樹的過程。

首先 root 的失敗指針為 NULL沪蓬,也就是指向自己彤钟。當我們已經(jīng)求得某個節(jié)點 p 的失敗指針之后,如何尋找它的子節(jié)點的失敗指針呢怜跑?

我們假設(shè)節(jié)點 p 的失敗指針指向節(jié)點 q样勃,我們看節(jié)點 p 的子節(jié)點 pc 對應(yīng)的字符吠勘,是否也可以在節(jié)點 q 的子節(jié)點中找到。如果找到了節(jié)點 q 的一個子節(jié)點 qc峡眶,對應(yīng)的字符跟節(jié)點 pc 對應(yīng)的字符相同剧防,則將節(jié)點 pc 的失敗指針指向節(jié)點 qc。



如果節(jié)點 q 中沒有子節(jié)點的字符等于節(jié)點 pc 包含的字符辫樱,則令 q=q->fail(fail 表示失敗指針峭拘,這里有沒有很像 KMP 算法里求 next 的過程?)狮暑,繼續(xù)上面的查找鸡挠,直到 q 是 root 為止,如果還沒有找到相同字符的子節(jié)點搬男,就讓節(jié)點 pc 的失敗指針指向 root拣展。



我將構(gòu)建失敗指針的代碼貼在這里,你可以對照著講解一塊看下缔逛,應(yīng)該更容易理解备埃。這里面,構(gòu)建 Trie 樹的代碼我并沒有貼出來褐奴,你可以參看上一節(jié)的代碼按脚,自己實現(xiàn)。
public void buildFailurePointer() {
  Queue<AcNode> queue = new LinkedList<>();
  root.fail = null;
  queue.add(root);
  while (!queue.isEmpty()) {
    AcNode p = queue.remove();
    for (int i = 0; i < 26; ++i) {
      AcNode pc = p.children[i];
      if (pc == null) continue;
      if (p == root) {
        pc.fail = root;
      } else {
        AcNode q = p.fail;
        while (q != null) {
          AcNode qc = q.children[pc.data - 'a'];
          if (qc != null) {
            pc.fail = qc;
            break;
          }
          q = q.fail;
        }
        if (q == null) {
          pc.fail = root;
        }
      }
      queue.add(pc);
    }
  }
}

通過按層來計算每個節(jié)點的子節(jié)點的失效指針敦冬,剛剛舉的那個例子辅搬,最后構(gòu)建完成之后的 AC 自動機就是下面這個樣子:


AC 自動機到此就構(gòu)建完成了。我們現(xiàn)在來看下脖旱,如何在 AC 自動機上匹配主串堪遂?

我們還是拿之前的例子來講解。在匹配過程中夯缺,主串從 i=0 開始蚤氏,AC 自動機從指針 p=root 開始,假設(shè)模式串是 b踊兜,主串是 a。

  • 如果 p 指向的節(jié)點有一個等于 b[i] 的子節(jié)點 x佳恬,我們就更新 p 指向 x捏境,這個時候我們需要通過失敗指針,檢測一系列失敗指針為結(jié)尾的路徑是否是模式串毁葱。這一句不好理解垫言,你可以結(jié)合代碼看。處理完之后倾剿,我們將 i 加一筷频,繼續(xù)這兩個過程蚌成;
  • 如果 p 指向的節(jié)點沒有等于 b[i] 的子節(jié)點,那失敗指針就派上用場了凛捏,我們讓 p=p->fail担忧,然后繼續(xù)這 2 個過程。

關(guān)于匹配的這部分坯癣,文字描述不如代碼看得清楚瓶盛,所以我把代碼貼了出來,非常簡短示罗,并且添加了詳細的注釋惩猫,你可以對照著看下。這段代碼輸出的就是蚜点,在主串中每個可以匹配的模式串出現(xiàn)的位置轧房。

public void match(char[] text) { // text 是主串
  int n = text.length;
  AcNode p = root;
  for (int i = 0; i < n; ++i) {
    int idx = text[i] - 'a';
    while (p.children[idx] == null && p != root) {
      p = p.fail; // 失敗指針發(fā)揮作用的地方
    }
    p = p.children[idx];
    if (p == null) p = root; // 如果沒有匹配的,從 root 開始重新匹配
    AcNode tmp = p;
    while (tmp != root) { // 打印出可以匹配的模式串
      if (tmp.isEndingChar == true) {
        int pos = i-tmp.length+1;
        System.out.println(" 匹配起始下標 " + pos + "; 長度 " + tmp.length);
      }
      tmp = tmp.fail;
    }
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绍绘,一起剝皮案震驚了整個濱河市奶镶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脯倒,老刑警劉巖实辑,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異藻丢,居然都是意外死亡剪撬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門悠反,熙熙樓的掌柜王于貴愁眉苦臉地迎上來残黑,“玉大人,你說我怎么就攤上這事斋否±嫠” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵茵臭,是天一觀的道長疫诽。 經(jīng)常有香客問我,道長旦委,這世上最難降的妖魔是什么奇徒? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮缨硝,結(jié)果婚禮上摩钙,老公的妹妹穿的比我還像新娘。我一直安慰自己查辩,他們只是感情好胖笛,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布网持。 她就那樣靜靜地躺著,像睡著了一般长踊。 火紅的嫁衣襯著肌膚如雪功舀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天之斯,我揣著相機與錄音日杈,去河邊找鬼。 笑死佑刷,一個胖子當著我的面吹牛莉擒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘫絮,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼涨冀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了麦萤?” 一聲冷哼從身側(cè)響起鹿鳖,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壮莹,沒想到半個月后翅帜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡命满,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年涝滴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胶台。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡歼疮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诈唬,到底是詐尸還是另有隱情韩脏,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布铸磅,位于F島的核電站赡矢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏阅仔。R本人自食惡果不足惜济竹,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盐数,春花似錦云矫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽号杠。三九已至闭树,卻和暖如春耸棒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背报辱。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工与殃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碍现。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓幅疼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昼接。 傳聞我的和親對象是個殘疾皇子爽篷,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 參考博文:AC自動機算法詳解 (轉(zhuǎn)載) (原文作者:DarkRaven,原文的鏈接失效了)圖片來源:AC自動機算...
    jenye_閱讀 4,640評論 2 4
  • 參考https://www.cnblogs.com/cmmdc/p/7337611.html 首先簡要介紹一下AC...
    idella閱讀 554評論 0 0
  • AC自動機(Aho-Corasick\ automaton)慢睡,可以解決多模板串匹配的問題逐工。可以理解為可以一次性匹配...
    An_Account閱讀 474評論 0 1
  • 文不達意漂辐,口齒不清泪喊,思想混亂,令人噴飯髓涯。(估計只有我自己才能看懂我在說什么)簡書沒有mathjax公式?jīng)]法愉快顯示...
    njzwj閱讀 1,172評論 1 2
  • A 日歷 本周我是否完成重要的日歷事項袒啼,有沒有爽約,或變更固定事項 青蛙有的未完成 B 清單 本周我有沒有做到要事...
    轉(zhuǎn)角遇到愛人閱讀 112評論 0 0