精確的字串匹配演算法有 單模匹配演算法,比如,kmp、bm演算法等;和 多模匹配演算法,比如,wu-manber、ac演算法等。
ac演算法(aho-corasick)是kmp演算法向多模式串情形的擴充套件,該演算法使用一種特殊的自動機,即ac自動機。ac自動機由一組模式串p生成,是trie的擴充套件。
先回顧一下kmp演算法。
每讀入乙個字元,kmp演算法更新 既是模式串的字首、同時也是已讀入文字的字尾 的最長字串的長度。設字串vβ是下乙個 既是模式串p的字首、同時也是已讀入文字t1…ti+1的字尾 的最長字串。可以看出:對模式串當前已匹配字首u來說,v既是u的乙個字尾、也是u的乙個字首,並且字元β一定與ti+1(即σ)相等。這裡,稱v是u的乙個邊界。
kmp演算法:
首先,預處理得到模式串p的每個字首u的最長邊界b(u)=max(v),即得到所謂的next陣列。
然後,設當前文字位置為i,對新讀入的字元σ=ti+1,按照如下步驟計算新的最長字首:
如果σ=p|u|+1=α,那麼新的最長字首是up|u|+1,計算結束;
如果σ≠α,設定u為最長邊界b(u),跳轉到步驟1;如果u為空字元,計算結束。
最終,當uσ=p的時候,即匹配完成。
ac演算法
ac演算法中類似next陣列的是一顆改造過的trie樹。用q表示模式p對應的trie狀態,用l(q)表示從初始狀態到q的路徑上的標號組成的字串。sac(q)定義為自動機中的另乙個狀態q』,使得l(q』)是l(q)的最長字尾,這是將「邊界」的概念擴充套件到一組字串。sac(q)稱為q的供給狀態,稱q到sac(q)的連線為供給鏈,稱由供給鏈連線而成的路徑為供給路徑。初始狀態的供給狀態為θ。
下圖為模式集合p=的ac自動機,虛線為sac,雙圓圈為終結狀態。例如,l(15)=acgata,既是它的字尾,同時也是某個模式串(這裡是atatata)的字首的最長字串是ata,對應狀態7,因此sac(15)=7。終結狀態是那些對應於整個模式串的狀態,此外,如果從狀態q到根節點的路徑上存在終結狀態,那麼q也是終結狀態。例如,由於sac(16)是終結狀態,所以16也是終結狀態。
假設已經讀入文字t1…ti,而既是其字尾、同時也是某個模式串的字首的最長字串對應ac自動機的current狀態,記該字串為v=l(current)。當讀入下乙個字元ti+1並計算t1…titi+1的新的最長字尾u時,有兩種情況:
如果狀態current存在標號為ti+1的轉移,目的狀態為f,即δac(current, ti+1)=f,那麼f將成為新的current狀態。並且,u=l(f)=uti+1是t1…titi+1的最長字尾,同時也是某個模式串的字首;
如果狀態current不存在標號為ti+1的轉移,那麼沿著current的供給路徑回溯,直到:
找到乙個狀態q,它存在標號為ti+1的轉移。那麼q的ti+1轉移的目的狀態f成為新的current狀態,並且u=l(f);
如果到達空狀態θ,那麼說明要尋找的最大字尾u是空字串ε,於是從current跳轉到初始狀態。
演算法偽**如下,f(current)表示current節點所對應的模式p中的相應字串:
aho-corasick(p=, t=t1...tn)
預處理ac ← build_ac(p)
匹配current ← ac自動機的初始狀態
for pos ∈ 1...n do
while δac(current, tpos) = θ and sac(current) ≠ θ do
current ← sac(current)
end while
if δac(current, tpos) ≠ θ then
current ← ac自動機的初始狀態
end if
if current是終結狀態 then
找到模式串f(current)
end if
end for
預處理階段,先根據模式串構造trie,然後bfs順序遍歷trie構造sac。
假設已經計算出current之前所有狀態的供給函式,現在考慮current父節點parent。假設parent到current的字元為σ,即current=δac(parent, σ)。sac(parent)已經計算出來了,要搜尋v=l(current)的最長字尾u,它同時也對應trie中的一條路徑。v可以寫成v』σ 的形式,如果u不是空串,那麼u一定能寫成u』σ 的形式,並且u』一定是v』的字尾。
如果sac(parent)有字元為σ的轉移,並且目的狀態為h,則w=l(sac(parent))是v』的最長字尾,並且wσ對應trie中的一條路徑。wσ就是最長路徑u,sac(current)指向h。
如果sac(parent)沒有字元為σ的轉移,或者抵達空狀態θ為止。如果抵達了空狀態θ,說明u是空字串ε,這時將sac(current)置為初始狀態。
演算法偽**如下:
build_ac(p=)
ac-trie ← trie(p)
δac是轉移函式
初始節點 ← ac-trie的根節點
sac(初始狀態) ← θ
for bfs的current節點 do
parent ← current的父節點
σ ← 從parent到current的輸入字元
down ← sac(parent)
while down ≠ θ and δac(down, σ) = θ do
down ← sac(down)
end while
if down ≠ θ then
sac(current) ← δac(down, σ)
if sac(current)是終止節點 then
標記current為終止節點
f(current) ← f(current) ∪ f(sac(current))
end if
else
sac(current) ← 初始節點
end if
end for
參考柔性字串匹配
AC自動機 多模匹配演算法
寫了個模板題,加強版借鑑大佬的 前置技能kmp 感覺沒啥用主要是思想 字典樹。p3808 模板 ac自動機 簡單版 include include include include include include include include using namespace std typedef ...
ac自動機 匹配最長字首 高階 AC自動機詳解
今天我們來介紹一點高階的知識 ac自動機。ac自動機是什麼呢?是不是用了這個演算法,不管什麼題目都會自動ac呢?別做夢啦 ac自動機,是aho corasick automaton的簡稱,該演算法在1975年產生於貝爾實驗室,是著名的多模匹配演算法。ac自動機是對字典樹演算法的一種延伸,是字串中運用...
AC自動機(多模式匹配)
ac自動機主要解決的問題 多模式匹配 kmp則屬於單模式匹配 n個單詞在m個字元的文章中,出現過多少次。主要分三步 構建trie樹 構建失敗指標 尋找匹配個數 trie樹 又稱字典樹 單詞查詢樹,是一種樹形結構,用於儲存大量的字串。它的優點是 利用字串的公共字首來節約儲存空間。具體參見 失敗指標 作...