字符串匹配问题,求匹配串中模式串的位置.
【1】暴力搜索, 每次匹配的过程假如匹配失败则 重新从模式串的开头位置和被匹配串的下一个位置开始匹配, 因为每个过程都是独立的,所以效率不高.
【2】KMP算法, 问题是由模式串引起的,因为从被匹配串找到模式串, 一次字符比对过程可以帮助下一次匹配.
2.1 最长的 前缀和后缀
假如有 abcabcd 字符串, d字符前面有"abcabc"字符串, 这个字符串 最长的相等前缀和后缀是"abc", "abc". 所以如果d这个位置字符想要保存信息, 应该保存这种前缀后缀长度匹配的信息, 3;
2.2 next数组
所以需要一个数组存储模式串的信息, 比如有
aaaab这个模式串, next[0] = -1,因为0位置之前没有字符,所以认为规定-1; next[1] 它的前后缀不存在,所以next[1]=0;
next[2] 前面有aa字符串, 前缀为"a"后最为"a"时匹配,所以next[2] = 1......
2.3 匹配流程(重点)
假设str1 是被匹配串, str2是模式串; str1与str2匹配到分别是x和y位置时不等, 这时str2并不是用它的0号元素和str1的i+1号元素匹配,因为可以证明 (i, j)位置不可能匹配出str2串: 我们假设(i, j)中间存在一个k( i < k < j) 位置,从它开始到x-1位置至少和str2的[0, ?)相等, 但是事实上并不存在,如果存在的话就和最长相等前后缀相矛盾了, 如图 前后缀是str2第一个圈和str1某个位置匹配的话,一定是j位置...
其实这就避免了(i,j)的不必要的匹配, 这就是核心想法.
2.4 求next数组 (重点)
public int[] getNext(char[] model) {
if (model.length == 1) {
return new int[] {-1};
}
int[] next = new int[model.length];
next[0] = -1;
next[1] = 0;
int i = 2;
int td;
while (i < model.length) {
/*
* 我们要求出Next[i]的值
* 前提,我们已经知道了next[i-1]的值.
*
* [ i-1 前缀] k [ i-1 的后缀] [i-1] [i]
* 1. 如果model[i-1] 的值 = model[k], next[i] = next[i-1]+1
* 2. 不等时-> [k的前缀] k2 [k的后缀] k = [ i-1 的后缀 ]
* 退而求其次: 我们再比较model[i-1]和 model[k2], 一直到数组0下标
*/
td = next[i-1];
if (model[i-1] == model[td]) {
next[i ++] = td + 1;
} else if (td > 0) {
td = next[td];
} else {
next[i ++] = 0;
}
}
return next;
}
kmp算法代码
public int kmp(String str, String model) {
if (model.length() > str.length()) {
return -1;
} else if (model.length() == str.length()) {
return model.equals(str)? 0:-1;
}
int[] next = getNext(model.toCharArray());
int i = 0; int j = 0;
while (i < str.length() && j < model.length()) {
if (model.charAt(j) != str.charAt(i)) {
j = next[j];
if (j == -1) {
i++;
j = 0;
}
} else {
j++;
i++;
}
}
if (j == model.length()) {
return i - j;
}
return -1;
}