通俗易懂的KMP演算法詳解

2021-09-08 05:40:30 字數 2279 閱讀 4607

1.bf演算法

首先,簡單介紹bf(簡單的模式匹配法);

假設主串為「abcbcglx」長度為m, 模式串為"bcgl"長度為n。

通常先從主串的下標0開始與子串的下標0開始匹配,如果匹配失敗,則從主串的第二個元素開始與子串的第乙個元素進行匹配。

例如:第一次匹配:剛開始先用主串下標0『a』與子串下標0『a』開始匹配。匹配失敗。

第二次匹配:從主串下標1『b』開始與子串的下標0『a』開始匹配。在紅箭頭處匹配失敗。

第三次匹配:接著從主串第三個元素即從主串下標2開始又重新從模式串下標0開始匹配。匹配失敗。

第四次匹配:匹配成功。當子串完全匹配時,返回主串下標3。

當子串與主串匹配失敗,即主串裡無此子串,子串匹配失敗,結果就會返回異常。在最不理想的情況下,時間複雜度為o(m*n),過高。

kmp是改進的bf。將時間複雜度從o(n*m)降到了o(n+m);

2.ptm

想要了解kmp,首先需要了解部分匹配表(ptm),這是kmp思想的核心。

對於字串「abababca」表示如下:

首先,需要弄清字首和字尾的意思。如果字串a和b,存在a=bs,其中s是任意的非空字串,那麼b就是a的字首。例如,」harry」的字首包括,我們把所有字首組成的集合,稱為字串的字首集合。同樣可以定義字尾a=sb, 其中s是任意的非空字串,那就稱b為a的字尾,例如,」potter」的字尾包括,然後把所有字尾組成的集合,稱為字串的字尾集合。要注意的是,字串本身並不是自己的字尾。

ptm中的值就是字串的字首集合與字尾集合的交集中最長元素的長度。

3.kmp

kmp演算法的目的,主串無需從頭重新匹配,需要判斷的是,在子串中不匹配的字串裡面,是否有相同的字首和字尾。例如:看下圖,我們需要看的是在子串中d之前的字串裡是否有相同的字首與字尾。『abc』沒有相同的字首和字尾,所以下一次匹配時,子串需要回到開頭,用『a』於『x』匹配。

『a』和『x』不匹配,所以主串移動至下一字元『a』開始與子串『a』匹配。然而在主串下標10『x』與模式串下標6『c』的地方不匹配。回頭看看是否存在字首和字尾相等的情況。『ab』即是字首又是字尾,這意味著,既然x與c之前的字串都是匹配的,所以在主串中x之前的字串也一定是『ab』。在下一次對比中主串可以跳過『ab』,讓主串『x』和子串『c』進行匹配,這樣主串這無需從子串的開頭開始匹配。

4.構建next陣列

如果高效的判斷子串是否有相同的前字尾呢:利用了ptm的思想,構建臨時陣列next。

需要構建臨時陣列next,用於記錄字尾和字首相同數。kmp演算法的主要目的就是當不匹配時,主串無需從頭重新匹配。在不匹配的地方看子串的字首和字尾交集的最長長度,這樣在主串不匹配的地方前面也有這樣長度與子串開頭相同的地方,這樣就可以跳過相同部分,無需從頭開始進行匹配。從子串字首的下乙個元素開始匹配就好,節省了複雜度。

kmp假設在模式串位置k上於主串位置m不匹配,假設字首和字尾相同數目為n,k前面有n個與對應於m前面n個相同的字元,所以不用再比較它們。這樣就能節省搜尋。

kmp的時間複雜度為o(n+m)。因為假設主串長度為m,模式串長度為n,主串搜尋的複雜度為o(m),模式串的臨時陣列next的複雜度為o(n);所以把他們相加就好。

參考:知乎文章:

通俗易懂的KMP演算法詳解

有些演算法,適合從它產生的動機,如何設計與解決問題這樣正向地去介紹。但kmp演算法真的不適合這樣去學。最好的辦法是先搞清楚它所用的資料結構是什麼,再搞清楚怎麼用,最後為什麼的問題就會有恍然大悟的感覺。我試著從這個思路再介紹一下。大家只需要記住一點,pmt是什麼東西。然後自己臨時推這個演算法也是能推出...

通俗易懂的EM演算法

假設現在有兩枚硬幣1和2,隨機拋擲後正面朝上概率分別為p1,p2。為了估計這兩個概率,做實驗,每次取一枚硬幣,連擲5下,記錄下結果,如下 硬幣結果統計1 正正反正反 3正 2反 2反反正正反 2正 3反 1正反反反反 1正 4反 2正反反正正 3正 2反 1反正正反反 2正 3反 可以很容易地估計出...

RocketMQ 術語詳解(通俗易懂)

rocketmq中有很多概念,其中包括一些術語和角色。理清楚基本的概念能有效的幫助理解rocketmq的原理以及排查問題。生產者。傳送訊息的客戶端角色。傳送訊息的時候需要指定topic。消費者。消費訊息的客戶端角色。通常是後台處理非同步消費的系統。rocketmq中consumer有兩種實現 pus...