private static int[] next(String p) {
int[] next = new int[p.length()];
next[0] = -1;
int k = -1, j = 2;
while (j < p.length() - 1) {
if (k == -1 || p.charAt(k) == p.charAt(j)) {
k++;
j++;
next[j] = k;
} else {
k = next[k];
}
}
return next;
}
上面是next数组的基本代码。
next[j]表示以j坐标的前一位结尾的最大公共前后缀的长度。
按照数组的表示我们知道从开头j长度为j+1。那么上面这个next[j]还可以解释为:
next[j]表示以j坐标的前一位结尾的最大公共前缀的后一位。
举个例子:
“abcabds”
d所在的索引是5,next[5]=2。是以b结尾的最大前缀的后一位,即c的索引。
明白了这一点,当p.charAt(k) != p.charAt(j)的时候,k = next[k]就会很好理解。
首先朴素的想法是k直接回退就是k=k-1,这种效率不高。
我们假设匹配的前缀为…ad,匹配的后缀为…ae
此时要在前缀找…ae怎么找,直接找e会很麻烦,我们可以找它的前一位,它的前一位必是匹配的。而由上面的分析可知,next正好存的是匹配前缀的后一位。这样就是找最长的前缀
…a的后一位的坐标,然后检测它是否与e相等就好了。
例子:
abeabz…abeabr
当检测到z!=r时,
我们要在abeabz,找…br,相当于找b?这样的串的子集,而这样的串都是以next[k]为索引结尾的,而next[k]的坐标正好是?的坐标。这样就会比k=k-1快的多。
还有一个问题:
abreabz…abeabr
这样当k=2的时候为什么前面的ab必定是匹配的?
很简单,因为next表示的就是存的就是公共前后缀里面前缀的后一位。
因为abreab是前后公共前后缀。而ab又是前缀部分的公共前后缀。
后一个是前一个的子集合。
我只能说设计这个算法的人是个大神。
同时想说csdn真是不的了,kmp那么多文章,互相来回抄,没有一篇让我看懂,可能我是个弱智。