KMP演算法學習
阿新 • • 發佈:2020-08-14
參考: https://www.zhihu.com/question/21923021
1.kmp
字串m,需要查詢存在m中的字串p
m長度為 len1 p長度len2
解決方法
1.暴力法
直接遍歷m,判斷p中的第一個字母是否相同,然後繼續往後判斷,如果失敗,繼續從當前字元開始,遍歷p,判斷是否相同,時間複雜度需要O(m*n)
2.Kmp法
- KMP演算法的核心,是一個被稱為部分匹配表(Partial Match Table)的陣列
- 字首與字尾
如果字串A=sb,則稱s為字串A的字首,b為A的字尾。要注意的是,字串本身並不是自己的字尾
PMT中的值是字串的字首集合與字尾集合的交集中最長元素的長度
- 這個最長前後綴元素表示含義:
如果當前字串為ababab,記為S1,長度為Slen,則
字首:{a,ab,aba,abab,ababa}
字尾:{b,ba,bab,abab,babab}
交集最長元素為:abab,記為C1 ,該長度為4
該長度的意義是什麼?
參考暴力法可知:當匹配失敗的時候,我們需要從字串開頭開始比對,但是C1可以代表從0開始到長度4,即[0,4)不用匹配,因為本身他與從S1的[slen-1-4,slen-1]相等。
所以對於字串m,p來說,當匹配到m[i]、p[j]處,如果m[i]!=p[j],則p應該從p[PMT[j-1]]處開始匹配,對於p[0-PMT[j-1])字元子串來說,已經算是匹配過了。
KMP實際對比流程
- 建立PMT表,計算出每個字元對應的PMT值,第一個字元為0
- i,j從0開始:當m遍歷到i,p遍歷到j時(j>0),此時 m[i]==p[j] 可知從p[0,j]與m[i-j-1,i]相等,繼續向後遍歷
- 如果m[i+1]!=p[j+1],此時我們知道對於p[0,j]==m[i-j-1,i]相等,因此可以通過pmt[j]=k,快速從p[0]處跳到p[k]處進行匹配。
next
對於PMT表我們通常使用next表來進行表示,對於PMT表來說,每次計算需要考慮PMT[j-1],所以next陣列為了計算方便,將PMT陣列整體向右移動一位,則每次計算只考慮next[j]即可。 計算方式如下:
//使用p的字首匹配p
static int[] generate(String p){
int[] next=new int[p.length()];
//從-1開始,字首,從0開始匹配,字尾
int j=-1,int i=0;
while(i<p.length()-1){
if(j==-1 || p.charAt(i)==p.charAt(j)){
//相等(符合前後綴公共子串)即更新PMT值,或者第一次,直接賦值為0
i++;
j++;
next[i]=j;
}else{
//無法匹配,繼續尋找前一個可以匹配位置。
j=next[j];
}
}
return next;
}
KMP
String a="helloabacs123",p="abac";
int[] next = generate(p);
int j=0,i=0;
while(i<a.length() && j<p.length()){
if(j==-1 || a.charAt(i)==p.charAt(j)){
j++;
i++;
}else{
j= next[j];
}
}
if(j>=p.length()) System.out.println(i-j);
System.out.println(-1);