书接上文,图解KMP算法及如何求next数组,在知道了什么是KMP算法及如何求解next数组之后,我们来讨论一下KMP算法的改进空间。
KMP算法的问题及改进
问题
假设有这样一个模式串"aaaaaaaaa",它的next数组是:[-1, 0, 1, 2, 3, 4, 5, 6, 7]。假设在模式串的最后一位发生了不匹配,例如下图的情况:
此时,如果按照KMP算法,需要从索引为7的元素开始依次向前比较,发现这些元素都不匹配,一直到索引为0时才停止。
虽然上面举的例子是一种比较极端的情况,但我们也可以发现KMP算法的一个不足之处:当发生不匹配时,我们可以根据next数组知道如何移动模式串,但是并没有考虑当前元素与移动后进行比较的元素的关系。
改进方法
要解决上面提出的问题也很简单,只需要在计算next数组时将当前元素也纳入考虑即可。
假设模式串为p,当前元素的索引为j:如果p[j] == p[next[j]],说明一旦当前元素和主串发生不匹配时,我们移动模式串后进行比较,也一定是不匹配的。因此,在这种情况下我们可以不用比较p[next[j]]是否和主串中对应元素相等,而是依次寻找next[j]不同时要比较的元素中和当前元素不同的,将找到的元素作为移动模式串的依据。
求nextval数组
在对KMP算法进行改进后计算得到的数组我们一般称为nextval数组,它的计算过程如下:
int[] getNextval(String pattern){
int length = pattern.length();
int[] nextval = new int[length];
nextval[0] = -1;
int i = -1, j = 0;
while (j < length - 1){
if (i == -1 || pattern.charAt(i) == pattern.charAt(j)){
i++;
j++;
//与求next数组不同的地方在于要考虑当前元素的情况
if (pattern.charAt(i) == pattern.charAt(j)){
nextval[j] = nextval[i];
}else {
nextval[j] = i;
}
}
else {
i = nextval[i];
}
}
return nextval;
}
可见,nextval数组的计算方式基本和next数组一样,唯一的区别在于如果当前元素和要跳转的元素相同时,我们转而寻找要跳转的元素不匹配时要继续跳转的元素。