KMP演算法超詳解與其應用

2021-10-10 16:14:57 字數 3504 閱讀 7246

kmp演算法是由d.e.knuth,j.h.morris和v.r.pratt三位大佬提出的一種改進的字串匹配演算法。

什麼是字串匹配呢?看下面的例子

假設我們有兩個字串,str1、str2

str1 = "ababababcbac"

str2 = "ababc"

求問:str2是否是str1的乙個子串

類似上面這樣的問題,就是我們常說的字串匹配問題,這裡需要注意乙個點:子串(一定連續)和子串行(不一定連續)的區別!

這樣的問題怎麼解決呢?首先我們可以想到的就是使用暴力匹配的方法了,如下:

private static int kmp(string str1, string str2) 

if (str1.length() == 0 && str2.length() == 0) return 0;

if (str1.length() == 0 || str2.length() == 0 || str1.length() < str2.length())

int res = -1;

for (int i = 0; i < str1.length(); i++)

tmp++;

}if (j == str2.length())

}return res;

}

當我們第一輪配的時候,會在下標為4的時候匹配失敗;然後開始第二輪匹配,str1會從第二個字元開始與str2的第乙個字元開始進行匹配,也就是每一次匹配失敗,str1都只會後移一位,並且str2都要從頭開始與str1進行匹配,不難看出這樣的時間複雜度是o(n * m)。匹配過程(標紅的地方表示匹配失敗):

那麼有沒有什麼可以優化的地方呢?

我們繼續來看這個例子:

首先對於str2中的c這個字元來說,它前面的字元剛好可以分為ab、ab兩個相等的子串,我們先叫前半部分為字元c的最長字首,後半部分叫做字元c的最長字尾(最長字首和最長字尾是完全一樣的,前後只是為了區分乙個從前往後,乙個從後往前)。現在的問題是我們匹配到c的時候匹配不上了,按照暴力匹配的思想我們只能一位一位的往後移動並進行匹配。這樣到我們能夠匹配成功,需要移動str1的下標指標5次才行。其實我們可以只需要移動3次即可,即上面的步驟1、3、5。為什麼呢?我們來看下面這張圖:

到這,可以看看返回去看看利用這種思路是不是只需要1,3,5就可以了。除此之外,我們也不難發現,kmp演算法中最重要的就是上面重複出現的最長字首和最長字尾了,那麼我們怎麼來維護這個最長字首和最長字尾的資訊呢?嘿嘿嘿,是不是覺得用個map不就好了,key是下標,value是最長字首和最長字尾,感覺自己是個天才,哈哈哈~

但是,通過前面的匹配過程我們不難發現,其實不管是最長字首還是最長字尾,我們實際上並沒有用到具體的最長字首和最長字尾的內容,僅僅是用到了它們的長度。所以呀,我們僅僅需要用乙個陣列來儲存一下最長字首和最長字尾的長度即可。我們稱這個陣列為next陣列。

現在我們來看看這個next陣列怎麼求呢?

我們規定某乙個字元的最長字首不能包含最後乙個字元(即某乙個字元的前乙個字元),最長字尾不能包含第乙個字元,

規定第乙個字元的next陣列的值為-1,第二個字元的next陣列的值為0

下面我們以aaaaa,ababc兩個字串為例來看一下它們的next陣列

aaaaa

[-1, 0, 1, 2, 3]

0位置 規定是 -1

1位置 規定是 0

2位置 前面為 aa 根據上面的第1條,前字尾都是字元a ==> 所以next陣列對應的值為1

3位置 前面為 aaa 同理根據第1條,前字尾都是aa ==> next陣列 2

4位置 前面為 aaaa 同理,前字尾都是 aaa ==> next陣列 3

ababc

[-1, 0, 0, 1, 2]

怎麼來求next陣列呢?

當我們要求i(i >= 2)位置的next陣列的值的時候,我們可以根據i-1位置的next陣列值來求,只需要判斷i-1位置字元是否與i-1位置的最長字首的後乙個字元相等。

kmp演算法實現

public static int getnextarray(char arr) ;

}int next = new int[arr.length];

next[0] = -1;

next[1] = 0;

int i = 2;

// 最長字首值

int cn = 0;

while (i < arr.length) else if (cn > 0) else

}return next;

}public static int getindexof(string s, string m)

char str1 = s.tochararray();

char str2 = m.tochararray();

int i = 0;

int j = 0;

int nextarray = getnextarray(str2);

while (i < str1.length && j < str2.length) else if (nextarray[j] == -1) else

}return j == str2.length ? i - j : -1;

}

kmp演算法的應用:給定乙個原始串,要求使用這個原始串構成乙個最短字串,且這個最短字串必須包含兩個原始串,這兩個原始串起始位置不能相同

eg:abcabc =》 abcabcabc

解法:求乙個原始串的next陣列,且多求一位,如:

abcabc =》 [-1, 0, 0, 0, 0, 0, 3]

第一次使用的後三位和第二次使用的前三位即重合,也就是拼出來的最短串

給定兩棵樹,判斷第二棵樹是否是第一棵數的一棵子樹:

將兩棵樹序列化為字串

t1 => str1

t2 => str2

null也要用特定的符號佔位,這樣是可以唯一確定一棵樹的,以及乙個節點結束之後也要用乙個特殊符號標識

如果str2是str1的乙個子串就可以確定t2是t1的一棵子樹

第二棵樹是和黃色的框框住的子樹一樣的,而綠色的框是不對的,因為綠色的框還有乙個右兒子節點沒框進去,所以不是一棵完整的子樹。

KMP演算法詳解及各種應用

kmp演算法詳解 kmp演算法之所以叫做kmp演算法是因為這個演算法是由三個人共同提出來的,就取三個人名字的首字母作為該演算法的名字。其實kmp演算法與bf演算法的區別就在於kmp演算法巧妙的消除了指標i的回溯問題,只需確定下次匹配j的位置即可,使得問題的複雜度由o mn 下降到o m n 在kmp...

KMP演算法詳解

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

KMP演算法詳解

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