假设主串s为:
模式串及其next数组t值为:
当其在主串[3]处失配,按照初版kmp算法,需要回溯至next[next[j]]处即next[2]处开始重新匹配,由图可以看出,其在next[2]处开始匹配也会出现失配,因为s[3]≠t[3],但是t[2]=t[3],所以s[3]≠t[2];由此可知,在模式串中,如果出现大量相同的元素,在初版kmp算法中,仍有可改进之处
当出现失配时,进行回溯,在回溯时判断,回溯点是否与上次失配时的值相同,当相同时,直接跳过匹配(因为此时必失配),并将失配处的k值置为next[j](j为模式串t的下标),即当s[3]≠t[3]时,next[3]处的k值将会变成next[next[3]]=1,当s[3]≠t[2]时,next[2]处的k值会变成next[next[2]]=0,以此往复,最终会成为一个新的next数组。
当形成新的next数组之后,其匹配速度就会进一步提高,因为又会舍弃一部分必错情况,此时代码也需进行相应更改
public class kmp {
public static void main(String[] args) {
String str1 = "aaabaaaab";
String str2 = "aaaab";
System.out.println(kmp(str1, str2));
int[] next = new int[str2.length()];
getNext(str2, next);
System.out.println(Arrays.toString(next));
}
static int kmp(String str1, String str2) {
int[] next = new int[str2.length()];
int i = 0, j = 0;
getNext(str2, next);
while (i < str1.length() && j < str2.length()) {
if (j == -1 || str1.charAt(i) == str2.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
}
if (j >= str2.length()) {
return (i - str2.length());//匹配成功,返回模式串结果开始处的下标
} else {
return -1;
}
}
static void getNext(String str, int[] next) {
int j = 0, k = -1;//j表示为当前匹配
next[0] = k;
while (j < str.length() - 1) {
if (k == -1 || str.charAt(j) == str.charAt(k)) {
//j表示为当前串的最大下标,k表示后缀与前缀相同最大数,可从next[k]处开始匹配
j++;
k++;
if (str.charAt(j) != str.charAt(k)) {
next[j] = k;
} else {
next[j] = next[k];
}
} else {
k = next[k];
}
}
}
}