在介紹kmp算法之前,我想先簡單介紹一下Brute-Force算法,這是一個(gè)回溯的字符串模式匹配算法,是一個(gè)簡單暴力狂!
由上圖的匹配思想,主要代碼如下:
public static int indexOf(String target, String pattern){
int i = 0,j = 0;
while(i < target.length() - pattern.length()){
if(target.charAt(i+j) == pattern.charAt(j)){
j++;
}else{
i++;
j = 0;
}
if(j == pattern.length()){
return i;
}
}
return -1;
}
很明顯,當(dāng)匹配不成功時(shí),目標(biāo)串每次向前移動(dòng)一位,模式串每次從P0開始重新匹配,雙方都存在回溯現(xiàn)象。在上圖中可以看出最差的情況: 每次匹配比較M次,比較了N - M + 1 次,比較總數(shù)是 M*(N - M + 1),由于M << N,所以時(shí)間復(fù)雜度O(M * N) 。
下面分析下Brute-Force算法的缺點(diǎn):
下面就著重講一下KMP算法:
-
算法描述:
核心是找到模式串中的k( 0 <=k < j) 滿足"P0...Pk-1" = "Pj-k...Pj-1" = "Ti-k...Ti-1" (當(dāng)Pj != Ti時(shí)),則模式串從Pk開始遍歷。
所以問題就轉(zhuǎn)化為:找到串"P0...Pj-1"相同的前綴子串和后綴子串的長度k。
- 求next數(shù)組
根據(jù)上圖的模式串"abcabc",可以得到next數(shù)組(注意在"P0...Pj-1"中尋找)
j | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
模式串 | a | b | c | a | b | c |
next[j] | -1 | 0 | 0 | 0 | 1 | 2 |
- 計(jì)算next數(shù)組的算法
由next數(shù)組的定義可以:
- next[0] = -1;
- 2.對于任意的j (0<=j <m ) next[j] < j;
- 若next[j] = k 說明在子串"P0...Pj-1"中存在相同的前綴子串和后綴子串,"P0...Pk-1" = "Pj-k...Pj-1" (0=<k<j,k取最大值);
- 對于next[j+1]而言,判斷"P0...Pj"子串中,是否存在"P0...Pk" = "Pj-k...Pj-1Pj"的問題,可以分解為 :
- 是否存在"P0...Pk-1" = "Pj-k...Pj-1"的問題;
- Pk == Pj的問題;
- 對于next[j+1]而言,判斷"P0...Pj"子串中,是否存在"P0...Pk" = "Pj-k...Pj-1Pj"的問題,可以分解為 :
很符合動(dòng)態(tài)規(guī)劃(Dynamic Programming)的問題,把多階段過程轉(zhuǎn)化為一系列單階段問題,利用各階段之間的關(guān)系,逐個(gè)求解,很像分治法。
同時(shí)個(gè)人認(rèn)為很像高考數(shù)列的相關(guān)問題 ---- 數(shù)學(xué)歸納法。
某個(gè)數(shù)列 An
- An = K (常數(shù));
- 假設(shè)前N項(xiàng)滿足: An = f(n) (某個(gè)函數(shù)式);
- 如果第N+1項(xiàng) 滿足 An+1 = f(n+1),則An = f(n) 等式成立。
綜上所述該動(dòng)態(tài)規(guī)劃的遞推公式:
D(j+1) = max(D(j)) + (Pk == Pj ? 1 : 0)
- 改進(jìn)next數(shù)組
在上圖1-3中 Pk = Pj , Ti != Pj => Pk != Ti 則完全沒有必要回溯到Pk開始比較,直接從P0開始,所以假如next[j] = k 且 Pk = Pj 則 next[j] = next[k];
- KMP核心代碼
private static int[] getNext(String pattern){
int j = 0,k = -1;
int[] next = new int[pattern.length()];
next[0] = -1;
while(j < pattern.length() - 1){
if(k == -1 || pattern.charAt(j) == pattern.charAt(k)){
j++;
k++;
//改進(jìn)next數(shù)組
if(pattern.charAt(j) != pattern.charAt(k)){
next[j] = k;
}else{
next[j] = next[k];
}
}else{
k = next[k];
}
}
return next;
}
public static int indexOf(String target, String pattern){
int i = 0,j = 0;
int[] next = getNext(pattern);
while(i < target.length()){
if(j == -1 || target.charAt(i) == pattern.charAt(j)){
i++;
j++;
}else{
j = next[j];
}
if(j == pattern.length()){
return i - j;
}
}
return -1;
}
- KMP算法分析
時(shí)間復(fù)雜度O(M + N) ,當(dāng)M<<N或者比較次數(shù)少(匹配的成功率比較高),時(shí)間復(fù)雜度O(N)。
以上是本人對KMP算法的理解,歡迎大家指正和交流,謝謝!