问题描述
在文本中找到模式字符串首次出现的位置。
文本:String text
模式字符串:String pattern
概念
1、前缀:表示包含首位字符但不包含末位字符的子串
2、后缀:表示包含末位字符但不包含首位字符的子串
3、next数组
- next[i]:表示模式字符串的子串 pattern[0,1,……, i] 中,前后缀相等的长度有多长。
代码实现
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main2 {
public static void main(String[] args) {
String text="ABCDABEABCDABD";
String pttern="ABCDABD";
int next[]=kmpNext(pttern);
System.out.println(Arrays.toString(next));
int result=kmpSearch(text,pttern,next);
System.out.println(result);
}
public static int kmpSearch(String text, String pattern, int[] next) {
for(int i = 0, j = 0; i < text.length(); i++) {
while( j > 0 && text.charAt(i) != pattern.charAt(j)) {//必须有j>0,因为要确保j-1大于0!
j = next[j-1];
}
if(text.charAt(i) == pattern.charAt(j)) {
j++;
}
if(j == pattern.length()) {
return i - j + 1;
}
}
return -1;
}
public static int[] kmpNext(String pattern) {
int[] next = new int[pattern.length()];
next[0] = 0;
for(int i = 1, j = 0; i < pattern.length(); i++) {
//这时kmp算法的核心点
while(j > 0 && pattern.charAt(i) != pattern.charAt(j)) {
j = next[j-1];
}
if(pattern.charAt(i) == pattern.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
}
过程理解
如上图所示,text="ABCDABEABCDABD",pattern="ABCDABD"。当比较到 i = 6,j = 6的时候,发现不匹配。此时对应的代码是
while( j > 0 && text.charAt(i) != pattern.charAt(j)) {
j = next[j-1];
}
当前 j = 6,则 执行语句 j = next[j-1],j 的值更新为2。将 text[6]与pattern[2] 比较,发现还是不相等。继续循环。
当前 j = 2,则 执行语句 j = next[j-1],j 的值更新为0。将 text[6]与pattern[2] 比较,发现还是不相等。因为循环条件 j>0 不成立,结束循环。当前 i = 6, j = 0。执行以下代码。
if(text.charAt(i) == pattern.charAt(j)) {
j++;
}
if(j == pattern.length()) {
return i - j + 1;
}
将 text[6]与pattern[2] 比较,发现不相等,所以第一个 if 语句不成立。
当前 j = 0 ,第二个 if 语句也不成立。当前循环结束,i++。当前 i = 7,j = 0。
参考链接
1、 帮你把KMP算法学个通透
2、 很详尽的KMP算法