經典演算法 KMP演算法詳解

2022-09-07 13:12:09 字數 4117 閱讀 5014

內容:

1、問題引入

2、暴力求解方法

3、優化方法

4、kmp演算法

1、問題引入

原始問題:

對於乙個字串 str (長度為n)和另乙個字串 match (長度為m),如果 match 是 str 的子串,

請返回其在 str 第一次出現時的首字母下標,若 match 不是 str 的子串則返回 -1 

注:

子串行和子串的區別:子串行可以不連續,子串必須連續

2、暴力求解方法

暴力求解方法:將 str 從頭開始遍歷並與 match 逐次比較,若碰到了不匹配字母則終止此次遍歷轉而從 str 的 第二個字元開始遍歷

並與 match 逐次比較,直到某一次的遍歷每個字元都與 match 匹配否則返回 -1 。易知此種 做法的時間複雜度為 o(n*m) 

注:kmp演算法則給出求解該問題時間複雜度控制在 o(n) 的解法

3、優化方法

優化方法:借助next陣列進行優化

在乙個字串中,每個字元之前的最長字首和最長字尾的最大匹配長度就是next陣列中的值,next陣列在kmp演算法中的目的就是決定下次匹配物件

注:字首不能包含最後乙個字元,字尾也不能包含第乙個字元(字首和字尾不能是字串整體!)

next陣列示例:

如何求next陣列:

當串 match 長度 mlen=1 時,規定 next[0]=-1 。當 mlen=2 時,去掉 match[1] 之後只剩下 match[0] ,

大匹配子串長度為0(因為字首子串不能包含串尾字元,字尾子串不能包含串首字元),即 next[1]=0

而當 mlen>2 時, next[n] (n>=2)都可以推算出來:

如上圖所示,如果我們知道了next[n-1] ,那麼 next[n] 的求解有兩種情況:如果 match[cn]=match[n-1] ,

那麼由a區域與b區域(a、b為最大匹配字首子串和字尾字串)相同可知 next[n]=next[n1]+1 ;

如果 match[cn]!=match[n-1] ,那麼求a區域中下乙個能和b區域字尾子串中匹配的較大的乙個,

即a區域 的大匹配字首字串 c區域 ,將 match[n-1] 和c區域的後乙個位置( cn' )上的字元比較,

如果相等則 next[n] 等於c區域的長度+1,而c區域的長度就是 next[cn] ( next陣列的定義如此);

如果不等則將 cn 打 到 cn' 的位置繼續和 match[n-1] 比較,直到 cn 被打到 0 為止(即next[cn]=-1 為止),那麼此時next[n]=0 

求next陣列**:

1

public

static

int getnextarray(char

str) ;4}

5int next = new

int[str.length];

6 next[0] = -1;

7 next[1] = 0;

8int i = 2;

9int cn = 0;

10while (i

if (cn > 0) else18}

19return

next;

20 }

4、kmp演算法kmp演算法的原理如下:子串 match 的 next陣列找到之後就可以進行 kmp 演算法求解此問題了。 kmp 演算法的邏輯是對於 str 的 i~(i+k) 部分 ( i 、 i+k 合法)

和 match 的 0~k 部分( k合法),如果有 str[i]=match[0] 、 str[i+1]=match[1] …… str[i+k-1]=match[k-1] ,但 str[i+k]!=[k] ,

那麼 str 的 下標不用從i+k 變為 i+1 重新比較,只需將子串 str[0]~str[i+k-1] 的大匹配字首子串的後乙個字元 cn 重新與 str[i+k] 向後依次比較,

後面如果又遇到了不匹配的字元重複此操作即可:

當遇到不匹配字元時,常規的做法是將 str 的遍歷下標 sindex 移到 i+1 的位置並將 match 的遍歷下標 mindex 移到 0 再依次比較,

而 kmp 演算法則不是這樣,當遇到不匹配的字元str[i+k] 和 match[k] 時, str 的遍歷指標 sindex=i+k 不用動,

將 match 右滑並將其遍歷指標 mindex 打到子串 match[0]~match[k-1]的最大匹配字首子串的後乙個下標 n 的位 置。

然後 sindex 從 i+k 開始, mindex 從 n 開始,依次向後比較,若再遇到不匹配的數則重複此過程

kmp演算法核心**:

1

public

static

intgetindexof(string s, string m)

5char str1 =s.tochararray();

6char str2 =m.tochararray();

7int i1 = 0;

8int i2 = 0;

9int next =getnextarray(str2);

10while (i1 < str1.length && i2

if (next[i2] == -1) else19}

20return i2 == str2.length ? i1 - i2 : -1;

21 }

可以發現 kmp 演算法中 str 的遍歷指標並沒有回溯這個動作(只向後移動),當完成匹配時 sindex 的移動次數小 於 n ,

否則 sindex 移動到串尾也會終止迴圈,所以 while 對應的匹配過程的時間複雜度為 o(n) ( if(next[j] != -1) 的

執行次數只會是常數次,因此可以忽略)

完整的kmp**及測試樣例如下:

1

//kmp演算法

2public

class

kmp

7char str1 =s.tochararray();

8char str2 =m.tochararray();

9int i1 = 0;

10int i2 = 0;

11int next =getnextarray(str2);

12while (i1 < str1.length && i2

if (next[i2] == -1) else21}

22return i2 == str2.length ? i1 - i2 : -1;23}

2425

public

static

int getnextarray(char

str) ;28}

29int next = new

int[str.length];

30 next[0] = -1;

31 next[1] = 0;

32int i = 2;

33int cn = 0;

34while (i

if (cn > 0) else42}

43return

next;44}

4546

public

static

void

main(string args)

53 }

經典KMP演算法整理

kmp演算法 大名鼎鼎的演算法 kmp由三位前輩的名字縮寫組成 其中第一位就是寫the art of the computer programming的高德納 它是一種效率很高的字串匹配演算法 傳統樸素做法的時間複雜度為o n m 而kmp演算法能將時間複雜度縮小到o n m 下面進入正題 kmp演...

KMP演算法詳解

模式匹配的kmp演算法詳解 這種由d.e.knuth,j.h.morris和v.r.pratt同時發現的改進的模式匹配演算法簡稱為kmp演算法。大概學過資訊學的都知道,是個比較難理解的演算法,今天特把它搞個徹徹底底明明白白。注意到這是乙個改進的演算法,所以有必要把原來的模式匹配演算法拿出來,其實理解...

KMP演算法詳解

kmp演算法即knuth morris pratt演算法,是模式匹配的一種改進演算法,因為是名字中三人同時發現的,所以稱為kmp演算法。因為偶然接觸到有關kmp的問題,所以上網查了一下next陣列和 nextval陣列的求法,卻沒有找到,只有在csdn的資料檔案裡找到了next陣列的簡單求法 根據書...