序:很久沒做算法題了,為了回顧一下自己的算法知識,方便下次理解,特地記錄自己一些對一些算法的理解。
約定:
模式串 ababcd
文本串 abababcd
用M代表模式串,W代表文本串
kmp算法包括兩個部分,1.計算模式串的next數組。 2.kmp主程序,模式串與主串(即文本串)的匹配。
Next數組
next[i]表示字符串第i個字符可匹配的最近的下標(挺拗口的),作用是記錄已經遍歷過的字符內的信息。
先看計算next數組的程序,
private static int[] getNext(char[] str) {
int length = str.length;
int[] next = new int[length];
next[0] = -1;
int k = -1;
for(int i=1; i<length; i++) {
while(k > -1 && str[i] != str[k+1]){
k = next[k];
}
if(str[i] == str[k+1]) {
k++;
}
next[i] = k;
}
return next;
}
字母下方即是模式串的next數組
a b a b c d
-1 -1 0 1 -1 -1
從結果來看,我們可以看到next[3] = 1,含義是它的可匹配的最近的下標是1。其實從這時候來看還是覺得沒什么作用,我們下面在匹配文本的時候具體分析。
kmp主程序
下面是kmp主算法:
1: public static int kmp(final char[] text, final char[] pattern) {
2: // 檢查是否為空
3: if(isEmpty(text) || isEmpty(pattern)) {
4: return -1;
5: }
6:
7: int lengthText = text.length;
8: int lengthPattern = pattern.length;
9: int[] next = getNext(pattern); // 獲取模式串的next數組
10:
11: int k = -1;
12: for(int i=0; i<lengthText; i++) {
13: while(k > -1 && text[i] != pattern[k+1]) {
14: k = next[k]; // 若不相等則找出與k可匹配的最近的下標
15: }
16: if(text[i] == pattern[k+1]) {
17: k++;
18: }
19: if(k == lengthPattern - 1) {
20: // 表示找到了,再賦值k,繼續下次尋找
21: System.out.println("find position is " + (i - lengthPattern + 1));
22: k = next[k];
23: }
24: }
25:
26: return -1;
27: }
看了上面的代碼,可以發現其實和計算next數組的代碼差不多。其實他們的本質是一樣的,計算next數組是模式串匹配自己,而kmp主程序是模式串匹配文本串。
匹配過程
從文本串的第一個字母開始匹配,文本串下標為int i,初始化為0。模式串的下標為k+1,k為int型,k初始化為-1。
下面是遍歷到i = 4, k = 3,時的情況,當發現兩者不相同時,k=next[k],所代表的意義是模式串的下標k位置的字符與前面的字符串哪一個字符最近可匹配,就是這個步驟可以省去和前面字符比較的時間(因為前面都相同)。所以當你發現文本串i位置和模式串j位置匹配不成功時,你可以比較模式串(next[j-1] +1)和文本串的i位置是否相等,如不相等如此循環查找(kmp主程序的13~15行)。所以此時的情況是k = next[k]之后k = 1,然后比較M(k+1)與W(i),我們發現此時是相等的,這一步就是kmp和普通模式串匹配的差別,它省去了再去從模式串的開頭進行比較,而是直接從下標2開始比較。
0 1 2 3 4 5 6 7
a b a b a b c d
a b a b c d
kmp的算法復雜度(O(n))
設主串的長度為n,模式串為m,n>m。
我們可以這樣計算,首先是對主串的遍歷,復雜度是n,主串的循環內的while循環,我們可以追蹤k的變化,k++的過程只能在每次循環時進行,k=next[k]是k的減少,k++最多發生n次,k=next[k]發生最多的次數是n/m *m = n,所以總的算法復雜度是3*n,就是O(n)的。
kmp的用處場景
1.查找模式串是否在目標串
2.計算字符串是由哪個或哪幾個字符串循環而來
3.查找模式串在目標串的哪些位置
4.最長公共子串
kmp的優化
以上我對kmp的理解來自於《算法導論》這本書,其實kmp還可以優化。一位大牛在網上論述了他對kmp的優化,下面是他的鏈接http://www.if-yu.info/2010/11/23/kmp-complexity.html
他的kmp思想跟導論不同在於next數組--直接記錄每個字符匹配失敗后應該去匹配哪個字符,這樣就可以優化重復匹配失敗的字符,而導論檢查的是前一個字符。
后記:轉載請標明出處,謝謝。 ----- doubleHHHH