如果要学KMP算法,就要先知道next数组,什么是next数组呢?举个例子:字符串“abcabc”,就是这个字符串的第i个字符前面的字符(不包括第i个本身)是否存在前缀和后缀相同的情况,如果存在,前缀或者后缀的长度就是next[i]的值,这句话后几个字不好理解。就是abcdab这个字符串,前缀是ab,后缀也是ab,这就叫做前缀和后缀相同。
求解“abcabc”的next数组
- 再次说明一下什么是next数组
- next[0]也就是a之前的字符串是否存在前缀和后缀相同的情况,我们一眼就看出来,他前面就没有字符串,我们约定next[0]=-1;
- next[1]也就是b之前的字符串是否存在前缀和后缀相同的情况,我们一眼就看出来,他前面只有一个a,我们约定next[1]=0;
- next[2]也就是c之前的字符串是否存在前缀和后缀相同的情况,他前面是ab,所以next[2]=0;
- next[3]也就是a之前的字符串是否存在前缀和后缀相同的情况,他前面是abc,所以next[3]=0;
- next[4]也就是b之前的字符串是否存在前缀和后缀相同的情况,他前面是abca,这个字符串有一个前缀后缀一样,就是那个a,所以next[4]=1;
- next[5]也就是c之前的字符串是否存在前缀和后缀相同的情况,他前面是abcab,这个字符串有一个前缀后缀一样,就是那个ab,所以next[4]=2;
- 这个数组上面咱们是用肉眼看到的,下面咱们用代码展现出来怎么求一个字符串的next数组:
- 如果next[j]=k;表示是这个字符串从p0到pk-1等于pj-k到pj-1
- 对于next[j+1]而言,需要满足p0到pk等于pj-k到pj,是否存在这个k值?
- 因为p0到pk-1等于pj-k到pj-1,所以我们只需要比较pj到pk是否相等。
private static int[] getNext(String pattern) {
int[] next = new int[pattern.length()];
next[0] = -1;
int j = 0, k = -1;
while (j < pattern.length() - 1) {
if (k == -1 || pattern.charAt(j) == pattern.charAt(k)) {
j++;
k++;
next[j] = k;
}
else {
k=next[k];
}
}
return next;
}
如果好好看了上面分析的那几句话,这个算法还是很好理解的,k代表的就是上一步的next数组的值
这其实并不是真正的next数组,还有待改进,改进后的next数组讲完KMP之后再说。
这是第一次匹配:
如果用BF算法第二次从第二个开始就好了,KMP算法则不是,这会next数组的用途就发挥了,匹配串的角标五个开始失败的,next[5]等于2,所以接下来从匹配串的第一个字母对应着S2开始匹配,通过next数组知道前两个不用比较,直接比较T2和S5。也就是下面这幅图:
上面那段话你可能不理解,直接看代码就好了,KMP算法:
private static int indexOf(String target, String pattern) {
int next[] = getNext(pattern);
int targetLength = target.length();
int patternLength = pattern.length();
int i = 0, j = 0;// 分别为目标串和模式串的下标
while (i < targetLength && j < patternLength) {
if (target.charAt(i) == pattern.charAt(j)) {
i++;
j++;
} else {
j = next[j];
// 判断剩下的够不够长
if (targetLength - i + 1 < patternLength - j + 1) {
break;
}
}
}
if (j == patternLength) {//匹配成功
return i - j;
} else {
return -1;
}
}
下面解决最后一个问题:优化后的next数组,
那字符串abcabc来举例
next[5]是等于2的,因为前面的字符串是abcab。假如说拿着他和abcabdabcabc来比较。第一次比较的是abcabd发现角标为五时对不住,按照上面的KMP算法,因为next[5]是2,所以下一次匹配是拿着abcabc中的第一个c和abcabdabcabc目标串的角标为5的字符d比较,明显因为第一次比较的时候就知道目标串的角标为5的字符不是c了。所以这是个例外,也就是next数组的应该有话的地方,就是next[j]和next[k]相等的时候应该特殊处理。
private static int[] getNext(String pattern) {
int[] next = new int[pattern.length()];
next[0] = -1;
int j = 1, k = -1;//k是next[0]的值,每循环一次就是上一步的next[k]的值
while (j < pattern.length() - 1) {
if (k == -1 || pattern.charAt(j) == pattern.charAt(k)) {
j++;
k++;
if (pattern.charAt(j) == pattern.charAt(k)) {
next[j] = next[k];
}else {
next[j] = k;
}
}
else {
k=next[k];
}
}
return next;
}
这里的话我上面解释的next数组的定义就不对了。因为next数组真正的是为KMP算法服务的,不是一个单独的事物。希望大家能懂。