在上一篇文章字符串匹配算法(一)——BF算法提到過,字符串匹配的思路是固定的:算法
將模式串
和主串
進行比較segmentfault
主串
和模式串
的下一個位置失配時,數組
在模式串
中尋找一個合適的位置數據結構
主串
當前失配位置進行比較模式串
的頭部與主串
失配位置的下一個位置進行比較主串
中找到一個合適的位置,從新與模式串
進行比較優化在於其中的步驟,而KMP算法
,就是優化第3步失配時尋找模式串
合適位置的操做。框架
那麼如何尋找模式串
中所謂合適的位置呢?能夠先來看個栗子:工具
......優化
上面是 BF 匹配過程當中從Nk到Nk+m的 m
次匹配過程,從中咱們能夠發現,從第 k
步到第 k+m
步時,指針 i
和 j
又回到了相同的位置,且 第 k+m
步 更具備匹配的可能性,因此咱們思考一下,是否是能夠由第 k
步直接跳到第 k+m
步呢?若是能夠,就能夠減小 m-1
次比較,大大提高效率。再進一步思考,若是將整個匹配過程再看做是重複地由Nk直接到Nk+m的推動,那麼每次重複時,模式串
開始比較的位置就是咱們所要找的合適的位置。spa
如何尋找這些位置呢?咱們能夠把這個問題轉化爲求next數組
的過程。3d
咱們再仔細觀察下 Nk 和 Nk+m 兩個狀態指針
因爲 Nk 狀態下,模式串
與主串
具備徹底匹配的部分,且要達到 Nk+m 狀態所需移動到的位置信息也存在於匹配的部分,所以咱們能夠無視掉主串
,只看模式串
便可獲得next數組
。
再認真觀察咱們還能發現,Nk 狀態不匹配時,Nk+m 狀態本質上是將模式串
中的另一對 AB
和 主串
達成以前的已匹配狀態。因此求next數組
的問題又能夠轉化爲當m位置不匹配時,求m位置以前的子串
的最大相同先後綴的問題。
首先要創建一個規則,具備先後綴的字符串長度至少爲2,因此咱們定義若是長度爲0,則對應next數組
值爲-1,若是長度爲1,值爲0。下面舉個栗子:
手工求這麼看其實沒什麼難度,本身多寫幾個串練一遍就會了。
學會如何手工求next數組
以後,整個KMP算法
的代碼如何寫呢?
還記得最開始提到要記住的一點嗎?匹配思路是同樣的,只是優化了失配後的操做。根據這一點,咱們能夠把BF算法
的框架先搬過來:
這樣是否是能夠接下來去補全 getNext()
方法就能夠了呢?咱們來看一個特殊狀況:
當處在Nk+m狀態時,發現失配位置前的 AB
沒有最長公共先後綴,因而只能退回到BF算法
的作法,也就是i++;j=0
。可是這和咱們上面的框架代碼不符,須要進行改造:
j = next[j] === -1
時,也須要進入第一個分支,使得 i++;j++(-1 + 1 = 0)
,變相達到效果。獲得最終的框架代碼:
接下來就是進行對next數組
的求解——完善 getNext()
。這時候有的同窗可能就會想對上述手工求法進行代碼轉化,但是萬一模式串
很長的話,那麼這個時間複雜度就會變得至關的高,因此須要採用迭代法,利用每次所得的結果來求下一個結果,從而拼湊出next數組
。
咱們假設某一時刻有一個狀態Sk
此時咱們已經求完了next[j]
的值,如何去求next[j+1]
呢?仔細觀察狀態圖,發現:
next[j] + 1 = 4
個相同的先後綴 P0P1Pk-jPk 和 Pj-kPj-k+1Pj-1Pj,也就是 next[j+1] = next[j] +1 = k + 1
再來看一個狀態
一樣是求完了next[j]的值,
若是 Pnextn[k] !== Pj呢?
能夠看到,
k = next[k]
直到回到前綴第一個位置,則表示沒有相同的先後綴,此時 j = -1
,則 next[j+1] = Pnextn[k] + 1 = k + 1 = 0根據以上分析,咱們能夠補充完 getNext()
再優化一下寫法
至此,一個完整的KMP算法
就寫好了。
咱們來看一個特殊的例子:
這是一個前綴相同的一個模式串
,且咱們已經求得了next數組
,接下來咱們模擬一下上面寫好的程序進行的操做:
咱們發現因爲前綴都是相等的,當第1步發現失配時,直接 j = -1
就能夠了,也就是 next[5] = -1
便可。因此,優化點實際上是體如今對next數組
的優化,咱們稱之爲nextVal數組
如何求nextVal數組
呢?咱們仍是以上面的特殊狀況爲例,看兩個狀態:
此時咱們已經求完了nextVal[j]的值,仔細觀察狀態圖,發現:
根據求next數組
的過程,next[j + 1] = k + 1
能夠在 getNext()
的基礎上獲得如下代碼:
next數組
如今就已是一個無關緊要的工具人了,咱們把去掉,獲得下一版代碼:
再進行如下優化獲得最終代碼:
總的來講,KMP算法
和BF算法
的字符串匹配思路在大方向上是沒有區別的,只是引入了一個next數組
或nextVal數組
來求得模式串
中合適的位置。只要理解了這兩個數組的求法,也就基本理解了KMP算法
。
「字符串匹配算法」是「重學數據結構與算法」系列筆記: