我們經常用的字串方法indexof,都是判定兩個字串的包含關係,底層使用類似kmp,bm, sunday這樣的演算法。如果我們要判斷乙個長字串是否包含多個短字串呢?比如在一篇文章找幾個敏感詞,在dna串中找幾個指定的基因對pattern進行預處理,如果我們的模式串存在多個,則不適合了,我們就需要用到一種多模式匹配演算法。
最著名的多模式匹配演算法為ac自動機,它是由貝爾實驗室的兩位研究人員alfred v. aho
和margaret j.corasick
於2023年發明的,幾乎與kmp演算法同時問世,至今日仍然在模式匹配領域被廣泛應用。
ac自動機的核心演算法仍然是尋找模式串內部規律,達到在每次失配時的高效跳轉。這一點與單模式匹配kmp演算法是一致的。不同的是,ac演算法尋找的是模式串之間的相同字首關係。
在kmp演算法中,對於模式串」abcabcacab」,我們知道非字首子串abc(abca)cab是模式串的乙個字首(abca)bcacab,而非字首子串ab(cabca)cab不是模式串abcabcacab的字首,根據此點,我們構造了next陣列,實現在匹配失敗時的跳轉。
而在多模式環境中,ac自動是使用字首樹來存放所有模式串的字首,然後通過失配指標來處理失配的情況。它大概分為三個步驟:構建字首樹(生成goto表),新增失配指標(生成fail表),模式匹配(構造output表)。下面,我們拿模式集合[say, she, shr, he, her]為例,構建乙個ac 自動機。
將模式串逐字元放進trie樹。
}cur.pattern = word; //防止最後收集整個字串用
cur.endcount++; //這個字串重複新增的次數
}} function creategoto(trie, patterns)
}然後我們嘗試用它處理字串sher。理想情況下是這樣:
很遺憾,字首樹只會順著某一路徑往下查詢,最多到葉子節點折回樹節點,繼續選擇另一條路徑。因此我們需要新增一些橫向的路徑,在失配時,跳到另乙個分支上繼續查詢,保證搜尋過的節點不會冗餘搜尋。
ac自動機的字首樹的節點都應該存在fail指標。下圖中,紅色的箭頭就是失配指標。它表示文字串在當前節點失配後,我們應該到哪個節點去繼續匹配。
很顯然,對於每個節點,其失配指標應該指向其他子樹中的表示同一字元的那些節點,並且它與其子樹能構成剩下的最長字尾。即,我們要匹配sher, 我們已經在某一子樹中命中了sh,那麼我們希望能在另乙個子樹中命中er。
到這裡,你是不是發現fail指標和kmp中的next指標簡直一毛一樣?它們都被稱為「失配指標」。將trie樹上的每乙個點都加上fail指標,它就變成了ac自動機。ac自動機其實就是trie + kmp
。
因此根據補上一些失配指標,我們的ac自動機應該長成這樣的。
現在的問題是,如何求fail指標?聯絡kmp演算法的next陣列的意義,容易發現root的每個兒子的fail都指向root(字首和字尾是不會包含整個串的)。也就是上圖中root所連的s
和h
的fail都指向root。若已經求得sh
所在點的fail,我們來考慮如何求she
所在點的fail。根據sh
所在點的fail得到h
是sh
的最長字尾,而h
又有兒子e
,因此she
的最長字尾應該是he
,其fail指標就指向he
所在點。
概括ac自動機求fail指標的過程:
1.對整個字典樹進行寬度優先遍歷。
2.若當前搜尋到點x,那麼對於x的第i個兒子(也就是代表字元i的兒子),一直往x的fail跳,直到跳到某個點也有i這個兒子,x的第i個兒子的fail就指向這個點的兒子i。
function createfail(ac) else
p = p.fail;
}if (!p)
}queue.push(child);}}
}}
我們從根節點開始查詢,如果它的孩子能命中目標串的第1個字串,那麼我們就從這個孩子的孩子中再嘗試命中目標串的第2個字串。否則,我們就順著它的失配指標,跳到另乙個分支,找其他節點。
如果都沒有命中,就從根節點重頭再來。
當我們節點存在表示有字串在它這裡結束的標識時(如endcound, isend),我們就可以確認這字串已經命中某乙個模式串,將它放到結果集中。如果這時長字串還沒有到盡頭,我們繼續收集其他模式串。
**如下:
function match(ac, text) ;
for (var i = 0; i < text.length; i++)
p = p.children[c];
if (!p)
var node = p;
while (node != root) 其起始位置在$`)
Android取消EditText自動獲取焦點
效果圖 最近在通訊錄新建聯絡人 中,一進入乙個頁面,edittext預設就會自動獲取焦點,很是鬱悶,如何讓edittext不自動獲取焦點?那麼如何取消這個預設行為呢?在網上找了好久,有點 監聽軟鍵盤事件,有點 呼叫 clearfouse 方法,但是測試了都沒有!xml中也找不到相應的屬性可以關閉這個...
android TextView 如何動態獲取寬度
1.當textview的屬性是wrap content時,在介面還沒顯示時,是無法獲取其顯示後的長和寬的,那麼怎麼獲取呢?可以通過測量的方法預先測量出來。很簡單。textview tv findviewbyid r.id.tv name tv name settext str int spec vi...
python程式設計星期幾 python如何獲取星期幾
以2019年9月19日為例 import time import datetime if name main today int time.strftime w anyday datetime.datetime 2019,9,19 strftime w print anyday 執行效果圖如下 下面...