KMP演算法 DFA版本

2021-10-07 11:41:28 字數 1912 閱讀 9820

為了更好地理解kmp演算法,我們先來看看樸素的暴力法是如何執行的。

設待匹配的字串為ababababac,模式串為ababac,i為字串下標,j為模式串下標

首先取i=0,遍歷一遍j,直到出現不匹配:

ababababac

ababac

然後取i=1,遍歷一遍j,直到出現不匹配:

ababababac

ababac

然後取i=2,遍歷一遍j,直到出現不匹配:

ababababac

ababac

不難發現,這種暴力法的缺點:做了很多無用功,從i往後已經走了很多步了,但是每次i還是只能退回去。

如果把模式串想象成兩個重疊在一起的透明條帶,滑動其中乙個 ,直到與另乙個部分完全重合。

像這樣:(以i=2時為例)

abababab

acababac

->       ababac

這樣,i就不用動了,直接把j調整為4,繼續匹配

具體「滑動」到什麼位置(把j調整到什麼位置),可以事先計算好,這就要說到dfa了

圖中,每個圓圈都是乙個狀態,圓圈中的數字表示已經匹配好的字元個數,箭頭表示了狀態轉移的方式,箭頭上的字母表示下乙個出現的字母是什麼。狀態怎麼轉移,已經通過「滑動視窗」定義好了,於是dfa也就定義好了。

可以證明,如果每一步都遵循dfa來走,那麼在任意時刻,能匹配到的字串都是最長的。(證明:如若不然,必然存在某個時刻t,使得t-1時刻是最長的,t時刻不是最長的,那麼就違背了滑動視窗的定義)

這一點很關鍵,在接下來dfa生成的部分會用到。

現在先不管dfa是怎麼高效生成的,既然我們已經可以用笨方法(滑動視窗)來生成dfa了。

假設我們已經有了dfa,那麼**就可以這麼寫:

(由於i不會回退,我們可以一邊讀資料一邊處理:

可見有了dfa,還是非常高效的。i不會回退,因此可以在o(待匹配字串長度)的時間內完成匹配。

但是,dfa的生成會不會拖後腿呢?

原理部分,我們可以這樣理解:

首先用「一次性完美匹配」的情況來構建dfa的骨架

設待匹配字串中,不匹配的那個字元為c。

目標是要滑動視窗,尋找最長的重疊字串,換言之,要在 [ 下標為1~j-1的字元 + c ] 中匹配最長的字串。等等,這個場景似曾相識,既然恰好j個字元(不會越界),那我們用當前的dfa半成品過一遍 [ 下標為1~j-1的字元 + c ] ,結果指向哪,我們就跳轉到哪!

於是,生成dfa的**如下:

**中的x代表用半成品dfa過一遍 [ 下標為1~j-1的字元 ](沒有c)的結果

複雜度如圖所示,沒怎麼拖後腿

ps:還有一種nfa版本的kmp演算法,更為常見一些,改天再研究

KMP演算法之DFA畫法,非常簡單

看這個需要你對kmp有點了解,我只講怎麼畫這個daf圖,其它基礎知識你們再去看資料了。我就直接先把演算法第四章的圖搬上來了,然後開始一步步說怎麼畫 第一步 因為a匹配所以若果是a就可以到狀態1,b,c就還是狀態0。那麼結果就是1 0 0 第二步 因為就兩個字母a和b,中間沒有了,所以就複製j 0時的...

PHP實現DFA演算法

最近專案需要用到敏感詞過濾功能,最開始想的是使用正則匹配和mysql儲存敏感詞來對敏感詞來進行過濾操作,但是這兩種方法都感覺不好。大家都知道正則效能問題一直都是乙個很大的問題,而使用mysql的話雖然可以實現,但是給資料庫增加了額外的壓力。後面經過google了解到dfa 有窮自動機 演算法可以解決...

Python實現模擬DFA演算法

演算法 模擬dfa 輸入 dfa d和輸入字串x,d的初態為s0,終態集為f。輸出 若d接受x,回答 yes 否則回答 no 構造dfa class dfa 初始化dfa def init self,s,s0,f,move self.s s 狀態集 list self.s0 s0 初態 int se...