kmp的难点其实就是next数组的求解和匹配时的运用
前提:
主串: text 简写 t串
匹配串: pattern 简写 p串
最长公共前后缀: ABC XXXXABC 的相同前后缀即为ABC
所谓的next数组就是当匹配失败时,为了尽量少匹配,那么就看看匹配串前面的x位能不能和最开头的x位相同,如果能的话,是不是不就用再匹配一遍这x位了呢?
一般来说next数组求解就两种
1.先求出[ 0 , n-1 ]的字符串的最长公共前后缀,然后整体后移1位,将next[0] 赋值为 -1
2.直接将next[0]赋值为 -1 ,然后直接计算
首先要确定的是两种方法求出的next数组的作用是一样的
即:
next[k] 表示 当 p串 下标 k 与 t串不匹配 时,k应该跳到的位置
也就是p串 下标为[ 0, k ) 的子串的 “最长相同前后缀”
需要理解的点
1.next[0]=-1
如果要用的这个点,只有当t串和p串的第一项都不相同,那么根据不相同时p串需要将下标跳转为next[j],就类似于t串的 i位 需要匹配 p串的 -1 位 ,但p串肯定没有第 -1 位阿,所以可以转换为p串的第0位需要匹配 t串的 第i+1 位
2.next[1]=0
只有一个字符的字符串,因为最长公共前后缀的长度必须比原串短,所以next[1]只能为0
计算方法:
一共要算n个值,已经确定了0 ,1 所以下一个下标肯定从2开始
假设比较值的下标为k---------同样也代表着目前的最长公共前后缀的值
那么每一次比较的就是p[k]和p[i-1];
如果相同,那么就意味着最长公共前后缀增加了,
所以首先肯定是k+±---------------更新最长公共前后缀的值
然后再将其赋给next[i]
最后将i++,去比较下一个值
如果不相同
一般可能会直接选择k=0,next[i]=0,i++ 然后结束
但其实还可以递归往下找
例如
abaxabay
| |
k i
此时p[k] != p[i]
但是 p [0 ~ k-1] 这一段 永远等于 p [ i-k ~i-1 ]
那么这两段中如果也存在最长公共前后缀,那么是不是也可以利用其来加速搜索呢?
那肯定是可以的
最长公共前后缀 本身就是利用 前缀和后缀 来跳跃
什么是跳跃?
当发生不匹配时,回溯回退的位置始终是模式串的不适配字符的上一位字符前后缀相同的位置-----也就是next[i]
那么两个相同的字符串,同样可以利用相同的最长公共前后缀来跳跃
就好像
字符串S1 的前缀 Sa1 后缀Sb1
字符串S2 的前缀 Sa2 后缀Sb2
如果S1S2
那么
Sa1Sb1== Sa2==Sb2
最关键的就是Sa1 ==Sb2
这不就是我们想要的最长公共前后缀么?
void get_next(char p[],int n) {
next1[0] = -1;
next1[1] = 0;
int k = 0;
int i=2;
while (i < n) {
if (p[i-1] == p[k]) { //理想情况,len就是想要增加
k++; //k原本是之前的最大前缀,这次匹配了,肯定要先加然后再赋值给next阿
next1[i] = k;
i++;
}
else
{
if (k > 0) {
k = next1[k];
}
else { //k已经是0了 无法再继续退了
next1[i] = k;
i++;
}
}
}
}
写到这里还看不懂可以看这个大佬写的博客
https://blog.csdn.net/yearn520/article/details/6729426