KMP演算法小結

2021-05-28 15:10:43 字數 2222 閱讀 1145

posted on

2011/06/14

byhuangchao

主要看了這裡,感覺講的十分的不錯,總結一下。

首先宣告要搜尋的串為s,設長度為n,要匹配的串為m,設長度為m.

先考慮暴力的演算法,暴力的演算法是遍歷s的每乙個字元,然後從這個字元開始和m串進行匹配。時間複雜度為o(nm).

怎麼在此基礎上進行優化?假設現在從某個位置(設為s)開始和m串進行匹配,如果匹配不成功,暴力演算法是從這個位置的下乙個位置(s+1)進行匹配,直觀上來說就是匹配的字串向後「滑動」了一位。

圖1能不能想辦法讓m向後移動的距離最大化?考慮最好的情況,如果和m匹配的s中的m個字元和m中的字元沒有乙個相等,那麼能向右移動m位;考慮最壞的情況,比如上圖,只能移動一位。

而kmp就是在這裡做文章,讓m串向後「滑動」的距離最大化。

圖2考慮上面的圖,m中灰色部分已經和s的灰色部分匹配上了,而灰色部分後乙個字元不匹配,則現在m要向後滑動,假設一直向後滑動,直到如圖位置又和s再一次匹配上了,那麼從這裡我們可以得到如下的結論:

這樣,如果暫時不考慮s,只看m的話,假設已經匹配的m的字串(即圖中m中灰色部分)為subm,則subm有個【相等】的【字首】和【字尾】。而且m在遇到不匹配的時候可以直接滑動到使subm的字首和subm的字尾重合的地方。而m向後滑動的時候,第一次subm的字首和字尾重合意味著此時這個相等的subm的字首和字尾的長度是最大的。

我們的任務就是要尋找subm的最長的字首和字尾相等的串。

知道了這一點,離kmp的真諦也就不遠了。現在結合這上面的圖模擬一下kmp演算法的整個流程:

從上面的步驟可以知道,kmp的關鍵就是要知道當s串中的字元和m串中的字元不匹配時,s串要和m串中的哪個字元繼續進行匹配。這個就是在利用狀態機模型來解釋kmp演算法時的狀態轉移.

kmp是通過乙個定義了乙個next陣列,這個next陣列儲存了如果s中的字元和m中的字元不匹配時s要和m中的哪個字元重新進行匹配的座標值。如圖2中所示是例子,s中的x位置和m不匹配了,那麼s要和m中a段後面的字元進行比較,從圖中來看是m向後滑動了。

換句話說,next[i]總是儲存了當m[i]不匹配時要從m[next[i]]處進行匹配,這個m[next[i]]可能會匹配,如果還不匹配?那麼可能會在m[next[next[i]]]處匹配了。這裡同時隱含著乙個資訊,就是i之前的一段字元和next[i]之前的一段字元是相同的,也就是m[0…i-1]相等的字首和字尾。

現在考慮next[0],next[1]…next[i]都已經知道了,那麼圖示如下:

設j=next[i],灰色部分表明這兩段字元是相等的,如果i位置的字元和j位置的字元相等,那麼next[i+1]=j+1;因為前一段灰色部分和j位置的字元組成的字串和後一段灰色的與i連線所形成的字串是相等的。這正是前面對next陣列的定義。如果不相等,則要找到從i開始包括i往前的一段字串與從0開始的一段字串相等,這樣形成相等的字首和字尾。所幸我們知道next[next[i]]的值,因為next[i]前面的字串也有最長的公共字首和字尾,而這個公共的字首與現在i以及往前形成的字串可能相等,這樣一直向前找,如果找不到,則說明i位置的字元從來沒有在之前出現過。

這樣求出來的next陣列其實是從下標1開始的,因為下標0之前是個空串,下標1則對應著m串的第0個字元。我們設next[0]=-1,僅僅是個標誌而已,沒有什麼特殊的含義。

那麼根據前面所述,可以很容易的寫出初始化next陣列的**

void kmpgetnext()

2:
11: }
知道了next陣列的值,則和s串進行匹配則相對簡單了,因為如果碰到不匹配的時候去查詢next陣列即可,直到找出和當前字元匹配的那個字元。如果找不到怎麼辦?找不到則會得到-1,也就是沒有字元和他進行匹配,那麼跳過這個字元,直接從下乙個字元進行匹配即可。

**如下:

void kmpsearch()

2:
13:     }
14: }
看到上面的**,兩層迴圈,貌似這個**並不是線性的,其實不然。外層迴圈了n次這個沒有問題,關鍵是裡面的while迴圈,這個迴圈的次數是多少並不好確定,然而考慮單單考慮j的值的變化,會發現第七行j增加1,而第6行j則減少,可能減少1,可能減少2,可能少的更多,但是j<0時迴圈就終止了,也就是說j有n次增加的機會,會有多少次減少的機會?或者問j最多減少多少次?j減少的次數最多的時候,就是每次減少1,這樣最多的會減少n次,也就是說第六行的迴圈最多會執行n次。平攤到每個迴圈,則執行次數為o(1),所以kmpsearch的時間複雜度仍然是線性的o(n),同理,kmpgetnext的時間複雜度為o(m).

KMP演算法小結

主要看了這裡,感覺講的十分的不錯,總結一下。首先宣告要搜尋的串為s,設長度為n,要匹配的串為m,設長度為m.先考慮暴力的演算法,暴力的演算法是遍歷s的每乙個字元,然後從這個字元開始和m串進行匹配。時間複雜度為o nm 怎麼在此基礎上進行優化?假設現在從某個位置 設為s 開始和m串進行匹配,如果匹配不...

KMP演算法小結

文字串長度為n,模式串長度為m,匹配過程的時間複雜度為o n 計算next的o m 時間,kmp的整體時間複雜度為o m n 步驟 文字串s bbc abcdab abcdabcdabde 模式串p abcdabd 1.尋找p的 最長公共元素表 前字尾最長序列長度 失配時,模式串向右移動的位數為 已...

KMP演算法小結

假設文字t y1y2y3.yn,模式 p p1p2p3.pm,傳統的匹配演算法把位移為0,1,n m時的文字依次跟p比較,每次比較最多花費o m 的時間,演算法的複雜度為o n m 1 m 傳統模式匹配演算法沒有利用匹配過的資訊,每次都從頭開始比較,速度很慢。而kmp演算法充分利用了之前的匹配資訊,...