第五章 字串專題 ---------------- 字串匹配(二)----KMP演算法
什麼是KMP演算法:
KMP演算法是一種改進的字串匹配演算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同時發現,因此人們稱它為克努特——莫里斯——普拉特操作(簡稱KMP演算法)。KMP演算法的關鍵是利用匹配失敗後的資訊,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個next()函式,函式本身包含了模式串的區域性匹配資訊。時間複雜度O(m+n)。
先來看看暴力解法:
假設主串是目標字串為S,模式串是待匹配的字串為P。用暴力演算法匹配字串過程中,我們會把S[0] 跟 P[0] 匹配,如果相同則匹配下一個字元,直到出現不相同的情況,此時我們會丟棄前面的匹配資訊,然後把S[1] 跟 P[0]匹配,迴圈進行,直到主串結束,或者出現匹配成功的情況。這種丟棄前面的匹配資訊的方法,極大地降低了匹配效率。時間複雜度O(m*n)
程式碼:
/**
* 暴力解法
* @param s 主串
* @param p 模式串
* @return
*/
private static int indexOf(String s, String p) {
int i = 0;
int sc = i;
int j = 0;
while(sc<s.length()){
if (s.charAt(sc)==p.charAt(j)) {
sc++;
j++;
if (j==p.length()) {
return i;
}
}else {
i++;
sc=i; // 掃描指標以i為起點
j=0; // 恢復為0
}
}
return -1;
}
而在KMP演算法中,對於每一個模式串我們會事先計算出模式串的內部匹配資訊,在匹配失敗時最大的移動模式串,以減少匹配次數。這樣主串的指標就不會回溯了,就能保證一次主串的迴圈就能解決問題。比如,在簡單的一次匹配失敗後,我們會想將模式串儘量的右移和主串進行匹配。右移的距離在KMP演算法中是如此計算的:在已經匹配的模式串子串中,找出最長的相同的字首和字尾,然後移動使它們重疊。
這裡可以看出指標指向的地方匹配失敗,而在已經匹配的模式串子串"ABCAB"中,最長的相同的字首和字尾是"AB",長度為2,所以j要向右移動到位置2,因為有相同的字首和字尾,那麼在移動的過程中,這幾個字元肯定是能夠匹配成功的,就不用去比較了。由此可以得出結論:當匹配失敗時,在已經匹配的模式串子串中,如果最前面的k個字元和j之前的最後k個字元是一樣的,那麼j要移動到下一個位置k。
然而,如果每次都要計算最長的相同的字首反而會浪費時間,所以對於模式串來說,我們會提前計算出每個匹配失敗的位置應該移動的距離,花費的時間就成了常數時間。因為在P的每一個位置都可能發生不匹配,也就是說我們要計算每一個位置j對應的k,所以用一個陣列next來儲存,next[j] = k,表示當S[i] != P[j]時,j指標的下一個位置k。那到底怎麼計算next陣列呢?
當j為0時,如果這時候不匹配,這種情況,j已經在最左邊了,不可能再移動了,next[0] = -1;那麼當j為1的時候,如果不匹配,j指標一定是後移到0位置的,因為它前面也就只有這一個位置了,next[1] = 0;如果p[j]==p[k]或者k<0,next[++j] = ++k,否則,k=next[k]。
程式碼:
public static int[] next(String ps) {
int pLength = ps.length();
int[] next = new int[pLength + 1];
char[] p = ps.toCharArray();
next[0] = -1;
if (ps.length() == 1)
return next;
next[1] = 0;
int j = 1;
int k = next[j]; //看看位置j的最長匹配字首在哪裡
while (j < pLength) {
//現在要推出next[j+1],檢查j和k位置上的關係即可
if (k < 0 || p[j] == p[k]) {
next[++j] = ++k;
} else {
k = next[k];
}
}
return next;
}
那麼完整的程式碼就是:
public class KMP {
public static void main(String[] args) {
String src = "babababcbabababb";
int index = indexOf(src, "bababb");
System.out.println("暴力破解法:"+index);
index = indexOf1(src, "bababb");
System.out.println("KMP演算法:"+index);
}
//O(m+n),求count 總共出現了多少次
private static int indexOf1(String s, String p) {
if (s.length()==0||p.length()==0) {
return -1;
}
if (p.length()>s.length()) {
return -1;
}
// int count = 0;
int []next = next(p);
int i = 0;//s位置
int j = 0;//p位置
int sLen = s.length();
int pLen = p.length();
while(i<sLen){
// ①如果j = -1,或者當前字元匹配成功(即S[i] == P[j]),都令i++,j++
// j=-1,因為next[0]=-1,說明p的第一位和i這個位置無法匹配,這時i,j都增加1,i移位,j從0開始
if (j == -1 || s.charAt(i) == p.charAt(j)) {
i++;
j++;
} else {
// ②如果j != -1,且當前字元匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]
// next[j]即為j所對應的next值
j = next[j];
}
if (j == pLen) {// 匹配成功了
// count++;
// j = next[j];
// 上面兩行程式碼是計數模式字串總共出現了多少次的
return (i - j);
}
}
return -1;
// return count; // -1
}
public static int[] next(String ps){
int pLength = ps.length();
int []next = new int[pLength+1];
char []p = ps.toCharArray();
next[0] = -1;
if (ps.length()==1) {
return next;
}
next[1] = 0;
int j = 1;
int k = next[j]; // 看看位置j的最長匹配字首在哪裡
while(j<pLength){
// 現在要推出next[j+1],檢查j和k位置上的關係即可
if (k<0||p[j]==p[k]) {
next[++j] = ++k;
}else {
k = next[k];
}
}
return next;
}
/**
* 暴力解法
* @param s 主串
* @param p 模式串
* @return
*/
private static int indexOf(String s, String p) {
int i = 0;
int sc = i;
int j = 0;
while(sc<s.length()){
if (s.charAt(sc)==p.charAt(j)) {
sc++;
j++;
if (j==p.length()) {
return i;
}
}else {
i++;
sc=i; // 掃描指標以i為起點
j=0; // 恢復為0
}
}
return -1;
}
}
結果:
相關文章
- 字串匹配問題——KMP演算法字串匹配KMP演算法
- 【字串匹配】KMP字串匹配KMP
- 字串匹配演算法:KMP字串匹配演算法KMP
- KMP字串匹配演算法KMP字串匹配演算法
- 字串匹配KMP演算法初探字串匹配KMP演算法
- 第五章 字串專題 ---------------- 字串匹配(三)----字尾陣列演算法字串匹配陣列演算法
- 字串匹配之KMP《演算法很美》字串匹配KMP演算法
- 第五章 字串專題 ---------------- 5.12 字串匹配之PabinKarp字串匹配
- KMP Algorithm 字串匹配演算法KMP小結KMPGo字串匹配演算法
- kmp字串匹配,A星尋路演算法KMP字串匹配演算法
- 字串匹配基礎下——KMP 演算法字串匹配KMP演算法
- KMP字串匹配演算法 通俗理解KMP字串匹配演算法
- 字串匹配-BF演算法和KMP演算法字串匹配演算法KMP
- 字串匹配演算法(三)-KMP演算法字串匹配演算法KMP
- 快速字串匹配一: 看毛片演算法(KMP)字串匹配演算法KMP
- 匹配字串之——KMP演算法深入理解字串KMP演算法
- KMP字串匹配演算法理解(轉)KMP字串匹配演算法
- KMP字串模式匹配詳解KMP字串模式
- 神奇的字串匹配:擴充套件KMP演算法字串匹配套件KMP演算法
- KMP字串匹配學習筆記KMP字串匹配筆記
- 字串匹配演算法之 BF 和 KMP 講解字串匹配演算法KMP
- 第五章 字串專題 ---------------- 5.5 題解:壓縮字串字串
- 圖解KMP字串匹配演算法+程式碼實現圖解KMP字串匹配演算法
- 第五章 字串專題 ---------------- 5.2 題解:巧妙翻轉字串字串
- 【模板】【字串】KMP演算法字串KMP演算法
- 【KMP求字串匹配次數】 hdu 1686KMP字串匹配
- 字串匹配問題字串匹配
- 【leetcode】28. Implement strStr() 字串匹配KMP BMLeetCode字串匹配KMP
- 第五章 字串專題 ---------------- 5.4 實踐:替換字串中的空格字串
- 第五章 字串專題 ---------------- 5.1 題解:判斷字串有無重複字元字串字元
- 第五章 字串專題 ---------------- 5.8 題解:將字串中按單詞翻轉字串
- 字串匹配演算法(二)-BM演算法詳解字串匹配演算法
- 字串匹配模式問題字串匹配模式
- 第五章 字串專題 ---------------- 5.7 題解:旋轉詞字串
- 字串演算法--$\mathcal{KMP,Trie}$樹字串演算法KMP
- 字串匹配演算法(一)字串匹配演算法
- 【KMP求字串第一個匹配位置】hdu 1711KMP字串
- 字串匹配字串匹配