讓人蛋疼的KMP演算法

2021-07-10 10:26:08 字數 3232 閱讀 3274

昨天刷題再次偶遇kmp

演算法,這個演算法之前來來回回折騰了好多次了,不理解好像理解了能寫**實現了又不理解又好像理解了。。。。。。。

今天在這裡整理下我個人對kmp

在寫之前先說明一下本文中的除最後乙個其它均來自

,這篇部落格講的挺好,大家可以看一下。

說到kmp

演算法必須從字串匹配說起。什麼是字串匹配呢?就是給你個文字串和目標串讓你找目標串在文字串中出現的位置,這聽起來好像很簡單直接用暴力法就可以。

暴力法如上圖,第一行為文字串第二行為目標串,這裡定義文字串的長度為n,目標串的長度為

m。以[start,end]中的每個字元做頭與目標串進行一一比較,當兩個子串相等時返回字串頭字元的索引。這種方法顯然正確但是忽略了我們在比較過程中獲取的一些有用資訊。

比如當比較進行到下圖這個狀態時:

目標串的前m-1

個字元都和文字串匹配,第

m個不匹配了,按照暴力法步驟接下來我們會進入下圖這個狀態

感覺好像也沒什麼就得這樣。但是經過進一步的推理我們會發現有些比較是沒必要進行的我們完全可以直接跳到這個狀態:

為什麼能這樣跳呢,這樣跳能保證一些解不被漏掉嗎???

再解釋原因之前必須引入字首字尾的概念。比如字串abc其字首就是

a,ab,abc

其中真字首為

a,ab

。同理真字尾為

c,bc

下面為了書寫方便用字首字尾來表示真字首和真字尾。字首字尾的長度即為這個子串字元的個數。我們這裡非形式化的對最長公共前字尾長度做個定義:

字串的有字首集合和字尾集合,兩個集合中可能會存在相等的串,在這些相等的串中長度最長的我們成為最長公共前字尾長度。例如對串abab

字首集合

字尾集合

最長公共前字尾為ab

長度為2.

有了這個定義之後我們就能得到這樣一張表:

下面我們解釋下上面這張表:第一行為字串中的每個字元,第二行為該字元前面的字串(不包括該字元)最長公共前字尾長度。因為對首字元a

來說考慮其前面的字串沒有意義故這裡標記為

-1.對於最後乙個字元

d其前面的字串為

abcdab

很明顯其有個字首字串

ab字尾字串

ab且兩者相等所有最長公共前字尾長度為2.

好了回到上面的問題,為什麼kmp

演算法就敢直接這樣跳呢,我們先看下用暴力法不跳的時候。

正如圖中說的方框內的字串上下相等,才有可能匹配。仔細看下方框內的是什麼東西呢,是不是abcdab

的前len-1(

這裡abcdab

的長度用

len表示

)個字首和

abcdab

的後len-1

個字尾比較呢,如果相等就有可能是不相等就不可能是。不是的話就再往後即用用目標串匹配以當前字元b

的下乙個字元開頭的字串。此時是

abcdab

的前len-2

個字首匹配

abdcab

的後len-2

的字首。。。。。。。。

len-3

。。。。。。。。

len-4.

。。。。。。。。。。。。。。。。。。。。

kmp演算法的精髓就是直接提前排除了不可能是的情況。它是怎樣做到直接排除的呢,還記得上面那個**嗎??

d對應的為2即

abcdab

的最長前字尾長度為

2.所有

len-1

時肯定不靠譜(很明顯這裡

len=6).

所以我們直接跳到len-4=2

時的狀態就可以,已知最長公共前字尾長度為

2,所有前兩個肯定是匹配的(字首

=字尾),直接從第三個開始比較就可以,這個狀態如下圖:

總結一下就是這樣:我們可以先計算一張表,這張表的內容是什麼呢,是每個字元前面串的最長公共前字尾。然後kmp演算法可以根據這張表跳躍式前進,為什麼說是跳躍式呢,這是

相對於 暴力法來講的。當匹配到某個字元匹配失敗時根據表中該字元所對應的值直接跳到相應位置。(實現跳躍的乙個原因是在建表的過程中我們已經進行了很多次比較)。

最後講一下如何建表:

這也是讓人很頭疼的乙個問題,我們這裡只概述一下其基本原理。我們用next

陣列來儲存這張表。對於首字元由於其前面是空串我們定義next[0]=-1

(這裡寫成

0也行只是**實現時細節部分要有相應考慮)

;對於第乙個字元其前面串只有乙個字元並且其真字首會空所以

next[1]=0(

這裡假定目標串至少有兩個字元

)。然後我們可以有

next[k]

的值來推

next[k+1]

的值。

如下圖:

藍色部分是相等的,並且藍色部分的長度就是對應的值,對於next,如果紅色部分等於黃色部分即這時候太好了但是當這倆貨不相等時就麻煩了。如下圖:

如圖所示藍色部分為索引k

之前字串的字首和字尾且兩者相等,如果,此時我們把上面的字串這樣看:

就到這吧,水平有限難免有疏忽望批評指正。

蛋疼的遞迴

幾個經典的遞迴場景 1.斐波那契 2.二叉樹的周遊 深度 前中後序 3.全排列問題 非簡單的全排列 允許字母重複 4.尋找滿足條件的n個數 第乙個的變種 跳台階 題目 乙個台階總共有n 級,如果一次可以跳1 級,也可以跳 2 級。求總共有多少總跳法,並分析演算法的時間複雜度。第二個要複習下非遞迴的寫...

令人蛋疼的ByteArray

最近做的乙個專案需要上傳比較多的到伺服器,伺服器限制大小是2m,在專案中,的大小都是有控制的,為100k 在互動中,返回給我 http status 413 上傳實體過大,超過伺服器大小。這讓我很糾結,我傳給伺服器的是二進位製流 bytearray 然後想當然的去計算,上傳的在10張以內,每張大小都...

dwr的蛋疼問題

最近在使用dwr,與之前相比 除了讓人非常蛋疼的問題,先描述如下 第乙個問題是伺服器向客戶端推送的時候,1 browser.withcurrentpage new runnable 13 使用該方法推送給當前的session使用,可是我之前測試總是在全部推送完畢後,才在頁面做接收,也就是看不到 一條...