关于字符串的下标:
建议都用从0开始的下标。
给谁算next数组?
给模式串(Pattern)算next数组。KMP保证的是母串(Text)一个一个字符来的时候永不后退,但模式串的位置是要经常变化的。千万不要去给Text串算next数组……
即:应该是给pattern串构建自动机,而不是text串。text串要在这个自动机上运行。
关于next数组:
真正编程的时候,next数组并不是“长度为len的前缀和长度为len的后缀相等”的len。事实上,这个len数组有另外一个名字,比如叫Partial Match Table(PMT);next是这个数组往后错了一位。
例如Pattern串是aabaabaac, 那么PMT =[0, 1, 0, 1, 2, 3, 4, 5, 0], next是这个数组整体往右错了一位,即[-1, 0, 1, 0, 1, 2, 3, 4, 5]。
(图片来自:https://www.zhihu.com/question/21923021)
next数组的第一个数值永远是-1,这纯粹是为了写起来方便,因为-1+1=0,正好可以对应到字符串的第一个元素。next数组的第一个元素是没有物理意义的。
Coding
写程序的时候,也没有必要特意的注意+1或者-1。因为比如发现T[i] != P[j],那么next[j]已经告诉你j-1位置结束的那个子串前后缀有多少相等了,所以你就听next[j]的就可以了。
抄一段上面知乎回答当中的code:
int KMP(char * t, char * p)
{
int i = 0;
int j = 0;
while (i < strlen(t) && j < strlen(p))
{
if (j == -1 || t[i] == p[j])
{
// j==-1时,说明当前t的这个字符根本没戏了,所以弃疗吧,i++就去看text串的下一个字符了
// 注意j==-1时,j+1=0,正好就是pattern串的第一个字符,即是text串的下一个字符和pattern串的第一个字符马上就要比对了
i++;
j++;
}
else
// 听pattern串自己的next数组,减小pattern串指针j,本质上是前移pattern串
j = next[j];
}
if (j == strlen(p))
return i - j;
else
return -1;
}
作者:海纳
链接:https://www.zhihu.com/question/21923021/answer/281346746
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
怎么求next
一样的道理,注意后增加i,因为next数组是错开一位的,所以每次i指的是当前的字符,但是要i+1以后再填充next值。
所谓“自我匹配”,实际上是当前到位置i的时候和位置j做匹配。比如上图i到c的时候,j到b的位置,所以恰好错了一位。永远要把j的位置理解成一个自动机就好办了。
void getNext(char * p, int * next)
{
next[0] = -1;
int i = 0, j = -1;
while (i < strlen(p))
{
if (j == -1 || p[i] == p[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];
}
}
作者:海纳
链接:https://www.zhihu.com/question/21923021/answer/281346746
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。