学习学习kmp算法

问题 :    k 为什么回溯到  next[k]  ?   k 是未知数,如何选 k 不浪费资源,即p[0~j]的最大重复子串长度

 核心思想:当 p[j] != p[k] 时我们要找的就是j+1位前面的子串,即p[0~j]的最大重复子串长度,但是 p[j] != p[k] 所以最大可能的重复子串只可能是p[0~k-1]里的前缀子串。从p[0~k-1]里,第一步,以0位为起始字符先挑选最大子串p[0~k-1],然后拿着这个子串,尾巴对齐,即看p[k-1]和p[j]对齐,与子串p[j-k+1~j]进行比较,见图中绿色线段;如果线段上每个值都相等了,则找到最大重复子串p[0~k-1];如果不等,则继续缩小线段长度找下去。只要前面一段能前缀且尽可能的长,那么加上最后一个端点这个前缀子串也必将是最长的。

自己梳理的思路:

        当p[j] != p[k]时,由图知应该比较前缀p[0~k-1]和后缀p[j-k+1~j],分两部分进行比较:要求第一部分尽可能长,对第一部分分析如图,最终转换成求p[0~k-1]最长前缀后缀重复长度,next[k]就是子串为p[0~k-1]的最大重复子串的长度,所以只有当 k = next[k] 时第一部分最长,p[0~next[k]-1]就是我们要找的第一个候选最大的重复子串。然后对于第二部分就是比较p[next[k]]和p[j]是否相等对应于代码中的p[k]==p[j])如果这两者相等了,则重复子串的长度+1,next[j+1]=next[k]+1(k++即next[k]+1);如果不相等了,则说明倒数第二大的p[0~next[k]-1]都不行了,比这个重复子串小的最大的重复子串只能是k=next[next[k]]了,如此继续查找下去。

大佬的原话:

        在查找最大匹配的过程中,将上面选择的待比较的子串分成两部分:最后一个端点为一部分,前面的一段为一部分;比如上面的第一个选取的最大比较子串的例子:前缀的p[0~k-1]分成两段为p[0~k-2]和p[k-1],和后缀的p[j-k+1~j-1]和p[j]分别比较,即p[0~k-2]和p[j-k+1~j-1]比较,p[k-1]和p[j]比较,见图中的红色线段和绿色圆点;只要前面一段能重复且尽可能的长,那么加上最后一个端点这个重复子串也必将是最长的。

        我们继续分析,因为next[j]已经求出,即p[0~k-1]===p[j-k~j-1],我们可以把上面的第一段的比较进一步转换成比较p[0~k-2]和p[1~k-1]子串了,见图中紫线箭头指示的漂移;这个就是求k位前的子串p[0~k-1]的最大重复子串,很显然不就是求next[k]嘛?!很明显p[0~next[k]-1]就是我们要找的第一个候选最大的重复子串,这也说明了子串p[0~k-2]就不可能是重复子串,也没有尝试比较的必要。因为根据next[j]的定义我们知道,next[k]就是要求的子串为p[0~k-1]的最大重复子串的长度。我们是充分利用了前面k<j时,next[k]已经求出来的条件,减少了子串比较的次数(其实也不叫减少了,那些比较本来就是无效的);这解释了为什么把k=next[k]。

        当 k = next[k] 时,p[0~next[k]-1]和p[j-next[k]~j-1]子串已经恒等了,我们只要比较另外的一部分即两个端点,p[next[k]]和p[j](对应于代码中的p[k]==p[j],注意在上个循环p[k]!=p[j]时,k已经被赋值next[k],而j还是上次的那个j);如果这两者相等了,则重复子串的长度+1,next[j+1]=next[k]+1(k++即next[k]+1);如果不相等了,则说明倒数第二大的p[0~next[k]-1]都不行了,比这个重复子串小的最大的重复子串只能是k=next[next[k]]了,如此继续查找下去。因此比较的都是按序递减的最大重复子串,非常的有效,一点都没有多比较。找不到的话,k会被赋值为-1。

void getNext(string p,int *next)
{
	int j, k;
	j = 0;
	k = -1;
	next[0] = -1;
	while (j < p.size()-1)
	{
		if (k == -1 || p[j] == p[k]) next[++j] = ++k; 
		else k = next[k];
		                
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值