演算法系列之二十六 字串匹配之KMP演算法

2021-09-02 09:58:00 字數 3667 閱讀 7012

一 簡介

kmp演算法是一種改進的字串匹配演算法,由d.e.knuth與v.r.pratt和j.h.morris同時發現,因此人們稱它為克努特—莫里斯—普拉特操作(簡稱kmp演算法)。kmp演算法的關鍵是利用匹配失敗後的資訊,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。

二 基於部分匹配表的kmp演算法

舉例來說,有乙個字串」bbc abcdab abcdabcdabde」,我想知道,裡面是否包含搜尋串」abcdabd」?

步驟1:字串」bbc abcdab abcdabcdabde」的第乙個字元與搜尋串」abcdab d」的第乙個字元進行比較。因為字元b與a不匹配,所以搜尋串後移一位。

步驟2:因為字元b與a不匹配,所以搜尋串再往後移一位。

步驟3:就這樣,直到字串有乙個字元與搜尋串的第乙個字元相同為止。

步驟5:直到字串有乙個字元與搜尋串對應的字元不相同為止。

步驟6:這時,最自然的反應是,將搜尋串整體後移一位,再從頭逐個比較。這樣做雖然可行,但是效率很差,因為你要把」搜尋位置」移到已經比較過的位置,重比一遍。(這就是之前講的[演算法系列之十二]字串匹配之蠻力匹配)

步驟8:怎麼做到這一點呢?可以針對搜尋串,算出一張《部分匹配表》(partial match table)。這張表是如何產生的,後面再介紹,這裡只要會用就可以了。

步驟9:已知空格與d不匹配時,前面六個字元」abcdab」是匹配的。

查表可知,最後乙個匹配字元b對應的」部分匹配值」為2,因此按照下面的公式算出向後移動的位數:

移動位數 = 已匹配的字元數 - 失配字元的上一位字元對應的部分匹配值
因為 6 - 2 等於4,所以將搜尋串向後移動4位,如下圖。

步驟11:因為空格與a不匹配,繼續後移一位。逐位比較,直到發現c與d不匹配。

於是,移動位數 = 6 - 2,繼續將搜尋串向後移動4位。

步驟12:逐位比較,直到搜尋串的最後一位,發現完全匹配,於是搜尋完成。

三 部分匹配表(partial match table)

下面介紹《部分匹配表》是如何產生的。首先,要了解兩個概念:字首字尾

部分匹配值就是字首和字尾的最長的共有元素的長度

以」abcdabd」為例。

由此得到部分匹配表,如下:

「部分匹配」的實質是,有時候,字串頭部和尾部會有重複。比如,」abcdab」之中有兩個ab,那麼它的」部分匹配值」就是2(ab的長度)。搜尋詞移動的時候,第乙個ab向後移動4位(字串長度-部分匹配值),就可以來到第二個ab的位置。

四 基於next陣列的kmp演算法

通過以上的匹配過程可以看出,問題的關鍵就是尋找搜尋串中最大長度的相同字首和字尾。找到了搜尋串中每個字元之前的字首和字尾公共部分的最大長度後,便可基於此匹配。而這個最大長度便正是next 陣列要表達的含義。

4.1:根據《部分匹配表》求next 陣列

經過上面敘述我們已經知道搜尋串「abcdabd」各個字首字尾的最大公共元素長度了,如下圖所示:

部分匹配表也由此而出如下所示:

有了部分匹配表我們就可以利用下面公式計算移動位數:

移動位數 = 已匹配的字元數 - 失配字元的上一位字元對應的部分匹配值
上文利用部分匹配表和移動位數計算公式進行匹配時,我們發現,當乙個字元失配時,其實沒必要考慮這個失配的字元,我們每次都是看的是失配字元的上一位字元「部分匹配值」。如此,便引出了next 陣列。

把next 陣列跟「部分匹配表」對比後,不難發現,next 陣列相當於「部分匹配表」 整體向右移動一位,然後第乙個元素值賦為-1。意識到了這一點,你會驚呼原來next 陣列的求解竟然如此簡單:就是找最大對稱長度的字首字尾,然後整體右移一位,第乙個元素值賦為-1(當然,你也可以直接計算某個字元對應的next值,就是看這個字元之前的字串中有多大長度的相同字首字尾)。

更新一下搜尋串移動位數的計算公式

移動位數 = 失配字元的位置 - 失配字元next值
其實兩公式實質上是一樣的,失配字元的位置等於已匹配的字元數,失配字元next 值等於失配字元的上一位字元的部分匹配值,只是換一種說法而已。

4.2 遞推求解next陣列

對於給定的字串p,其next陣列的含義是:對於k=next[j],p的字首p[0…k-1]和p的字尾p[j-k…j-1]匹配,k要盡可能的大,且k< j。我們可以根據上述含義寫出next的蠻力計算方法。複雜度應該是o(^2)。

換個思路,現在next[0]=-1,next[1]=0。

假設k=next[j],則p[0…k-1]=p[j-k…j-1],那麼求next[j+1]有兩種情況:

void getnext(string t,int next)//if

else

}//while

}

引用:字串匹配的kmp演算法

從頭到尾徹底理解kmp

kmp演算法求next陣列

kmp演算法的next陣列通俗解釋

演算法系列之十二 字串匹配之蠻力匹配

引言 字串匹配是資料庫開發和文字處理軟體的關鍵。幸運的是所有現代程式語言和字串庫函式,幫助我們的日常工作。不過理解他們的原理還是比較重要的。字串演算法主要可以分為幾類。字串匹配就是其中之一。當我們提到字串匹配演算法,最基本的方法就是所謂的蠻力解法,這意味著我們需要檢查每乙個文字串中的字元是否和匹配串...

演算法系列之十二 字串匹配之蠻力匹配

引言 字串匹配是資料庫開發和文字處理軟體的關鍵。幸運的是所有現代程式語言和字串庫函式,幫助我們的日常工作。不過理解他們的原理還是比較重要的。字串演算法主要可以分為幾類。字串匹配就是其中之一。當我們提到字串匹配演算法,最基本的方法就是所謂的蠻力解法,這意味著我們需要檢查每乙個文字串中的字元是否和匹配串...

二十六 python中字串支援的函式

字串支援的函式 1.upper lower capitalize 例 coding utf 8 s hellopython 返回乙個新的字串 print s.upper 全部字母大寫格式 print s.lower 全部字母小寫格式 print s.capitalize 首字母大寫格式 原字串是沒有...