KMP子字符串查找算法
概述
算法的基本思想是:當出現不匹配時,就能知曉一部分文本的內容,可以利用這些信息避免將指針回退到所有這些已知的字符串之前。
DFA(確定有限狀態機)模擬
提前判斷如何重新查找,而這種判斷只取決於模式本身,所以可以對模式的字符序列做一個確定有限狀態機。
DFA的數據結構表示爲二維數組dfa[R][M],其中R爲指定字典中的字符集的個數(比如ASCII爲256),M爲匹配字符串pat的長度,狀態的意思是文本中某個位置i匹配pat的程度,0狀態爲未匹配狀態,M狀態爲終止狀態,找到了完整匹配的字符串。
如圖中R=3,M=6,二維數組中的值指向下一個狀態。
構造DFA
窮舉模式pat的所有可能情況,將這些情況用狀態圖表示。其中X記錄匹配失敗時重啓的索引位置。
編碼實現
用暴力算法實現子字符串查找算法
public int search(String txt, String pat) {
int i, N = txt.length();
int j, M = pat.length();
for (i = 0, j = 0; i < N && j < M; i++) {
if (txt.charAt(i) == pat.charAt(j)) {
j++;
} else { //顯式回退
i-=j;
j=0;
}
}
if (j==M) return i-M;
return N;
}
KMP查找
/**
* @return pat在txt中開始出現的位置,如果等於txt.length()表示沒有找到
*/
public int search(String txt) {
int M = pat.length();
int N = txt.length();
int i, j; //i指向txt,j指向pat
for (i = 0, j = 0; i < N && j < M; i++) {
j = dfa[txt.charAt(i)][j];
}
if (j == M) return i - M; //匹配
return N; //不匹配
}
構造DFA
private final int R; // the radix
private int[][] dfa; // the KMP automoton
private String pat;
public KMP(String pat) {
this.R = 256; //設置字典大小
this.pat = pat;
//構造pat對應的dfa
int M = pat.length();
dfa = new int[R][M];
dfa[pat.charAt(0)][0] = 1;
for (int X = 0, j = 1; j < M; j++) { //X記錄匹配失敗時的索引位置,j指向pat
for (int c = 0; c < R; c++) { //對於匹配失敗的情況,直接複製重啓狀態
dfa[c][j] = dfa[c][X];
}
dfa[pat.charAt(j)][j] = j + 1; //匹配成功的指向下一個狀態
X = dfa[pat.charAt(j)][X]; //更新重啓位置X
}
}
優缺點
優點:適合在長度不確定的輸入流中進行查找,不需要在輸入中回退。
缺點:最壞的情況(在重複性很高的文本中查找重複性很高的模式)在實際應用中很少出現,還不如使用暴力算法來的容易,性能也差不了多少。