可進入我的部落格檢視原文。
字串匹配是字串的一種基本操作:給定一個長度為 M 的文字和一個長度為 N 的模式串,在文字中找到一個和該模式相符的子字串,並返回該字字串在文字中的位置。
KMP 演算法,全稱是 Knuth-Morris-Pratt 演算法,以三個發明者命名,開頭的那個K就是著名科學家 Donald Knuth 。KMP 演算法的關鍵是求 next 陣列。next 陣列的長度為模式串的長度。next 陣列中每個值代表模式串中當前字元前面的字串中,有多大長度的相同字首字尾。
Boyer-Moore 演算法在實際應用中比 KMP 演算法效率高,據說各種文字編輯器的"查詢"功能(Ctrl+F),包括 linux 裡的 grep 命令,都是採用 Boyer-Moore 演算法。該演算法有“壞字元”和“好字尾”兩個概念。主要特點是字串從後往前匹配。
Sunday 演算法跟 KMP 演算法一樣,是從前往後匹配。在匹配失敗時,關注文字串中參加匹配的最末位字元的下一位字元,如果該字元不在模式串中,則整個模式串移動到該字元之後。如果該字元在模式串中,將模式串右移使對應的字元對齊。
關於這幾種演算法的詳細介紹,可參考該部落格。
下面分別給出暴力匹配、KMP 演算法、Boyer-Moore 演算法和 Sunday 演算法的 Java 實現。
暴力匹配:
public static int forceSearch(String txt, String pat) {
int M = txt.length();
int N = pat.length();
for (int i = 0; i <= M - N; i++) {
int j;
for (j = 0; j < N; j++) {
if (txt.charAt(i + j) != pat.charAt(j))
break;
}
if (j == N)
return i;
}
return -1;
}複製程式碼
KMP 演算法:
public class KMP {
public static int KMPSearch(String txt, String pat, int[] next) {
int M = txt.length();
int N = pat.length();
int i = 0;
int j = 0;
while (i < M && j < N) {
if (j == -1 || txt.charAt(i) == pat.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
}
if (j == N)
return i - j;
else
return -1;
}
public static void getNext(String pat, int[] next) {
int N = pat.length();
next[0] = -1;
int k = -1;
int j = 0;
while (j < N - 1) {
if (k == -1 || pat.charAt(j) == pat.charAt(k)) {
++k;
++j;
next[j] = k;
} else
k = next[k];
}
}
public static void main(String[] args) {
String txt = "BBC ABCDAB CDABABCDABCDABDE";
String pat = "ABCDABD";
int[] next = new int[pat.length()];
getNext(pat, next);
System.out.println(KMPSearch(txt, pat, next));
}
}複製程式碼
Boyer-Moore 演算法
public class BoyerMoore {
public static void getRight(String pat, int[] right) {
for (int i = 0; i < 256; i++){
right[i] = -1;
}
for (int i = 0; i < pat.length(); i++) {
right[pat.charAt(i)] = i;
}
}
public static int BoyerMooreSearch(String txt, String pat, int[] right) {
int M = txt.length();
int N = pat.length();
int skip;
for (int i = 0; i <= M - N; i += skip) {
skip = 0;
for (int j = N - 1; j >= 0; j--) {
if (pat.charAt(j) != txt.charAt(i + j)) {
skip = j - right[txt.charAt(i + j)];
if (skip < 1){
skip = 1;
}
break;
}
}
if (skip == 0)
return i;
}
return -1;
}
public static void main(String[] args) {
String txt = "BBC ABCDAB AACDABABCDABCDABDE";
String pat = "ABCDABD";
int[] right = new int[256];
getRight(pat,right);
System.out.println(BoyerMooreSearch(txt, pat, right));
}
}複製程式碼
Sunday演算法
public class Sunday {
public static int getIndex(String pat, Character c) {
for (int i = pat.length() - 1; i >= 0; i--) {
if (pat.charAt(i) == c)
return i;
}
return -1;
}
public static int SundaySearch(String txt, String pat) {
int M = txt.length();
int N = pat.length();
int i, j;
int skip = -1;
for (i = 0; i <= M - N; i += skip) {
for (j = 0; j < N; j++) {
if (txt.charAt(i + j) != pat.charAt(j)){
if (i == M - N)
break;
skip = N - getIndex(pat, txt.charAt(i + N));
break;
}
}
if (j == N)
return i;
}
return -1;
}
public static void main(String[] args) {
String txt = "BBC ABCDAB AACDABABCDABCDABD";
String pat = "ABCDABD";
System.out.println(SundaySearch(txt, pat));
}
}複製程式碼
可進入我的部落格檢視原文。