KMP算法next数组详解~我终于学会啦!

KMP算法

起因是在力扣刷题时遇到了一道题目(1764),这题目一看就知道要用KMP算法,虽然没写过相关代码但是知道手算原理,然后兴致勃勃去看大佬们写的代码,越看越不明白,怎么和手算不太一样啊!于是各种找资料,总算是搞明白了KMP算法最不能理解的一行代码:j=next[j];
查阅的部分资料:
手算:天勤考研(目前看到的最通俗易懂的带图解的手算过程)
https://www.bilibili.com/video/BV1jb411V78H/?spm_id_from=333.337.search-card.all.click&vd_source=733d2f9b50abf06d9a8eadcf7ceb2046
代码(next数组构造)讲解:凡三岁爱学习(大佬虽然粉丝不多,但是把代码讲明白了,一键三连,下次一定👍)
https://www.bilibili.com/video/BV16X4y137qw/?spm_id_from=333.337.search-card.all.click&vd_source=733d2f9b50abf06d9a8eadcf7ceb2046

下面是我个人的学习笔记


一个前提:原串和匹配串前面都要加空格,使其下标从 1 开始,不太能理解为啥不从0开始,可能这就是KMP三位大佬想出来的解决办法吧~
如果你已经会手算了(不会请速速去看第一个视频),那么你一定知道以下几个知识点👇:

1、前缀:包含首位字符但不包含末位字符的子串。
2、后缀:包含末位字符但不包含首位字符的子串。
3、next数组定义:当主串与模式串的某一位字符不匹配时,模式串要回退的位置。
4、next[j]:其值=第j位字符前面j -1位字符组成的子串的前后缀重合字符数+1
   简而言之就是“最长公共前后缀的长度+1

这里先放一下求next数组的代码,因为我的大学课程里用的是严蔚敏先生的教材,因此采用严先生的next数组定义法:

int GetNext(char ch[],int length,int next[]){//length表示next数组长度
	next[1]=0;
	int i=1,j=0;
	while(i<=length){
		if(j==0||ch[i]==ch[j])
			next[++i]=++j;
		else
			j=next[j];
	}
}

这里的 i 表示主串当前正在匹配的位置,同时也是next数组的索引,j 用来回溯next数组
下面进入正题

索引 i123456
模式串ababac
next011234

根据代码可知,当ch[i]和ch[j]相等时,很容易想到,在之前的 j (即公共前后缀长度+1)的基础上再+1,然后继续往后走

但是当ch[i]和ch[j]不相等时,怎么办呢,这里给出的代码是j=next[j];我理解了好久还是没能搞明白为什么这样就能得出来新的最长公共前后缀。

借用一下B站up的图理解一下:这里假设next[16]的值是8,求next[17],此时i=16,j=8
按我们的思路肯定是先求ch[1-16]的最长公共前后缀

  • ch[16]和ch[8]相等:因为 next[16] 的值为 8 ,很显然如图 ch[1-7] 和 ch[9-15] 是相等的。如果 ch[16] 和 ch[8] 相等的话,那么 ch[17] 的最长公共前后缀长度是不是就变成 8 甚至超过 8 (这里我们当作是8)即ch[1-8]和ch[9-16]是相等的,next[17]=最长公共前后缀长度+1=8+1=9

在这里插入图片描述


  • ch[16]和ch[8]不相等:此时按照代码应该 j=next[j] ,我们假设 next[8]=4 ,那么此时 j 就应该等于 4 , i 此时还是在 16的位置,就会出现下图。在刚才图的基础上,会发现 ch[1-3] 、 ch[5-7] 、 ch[9-11]、 ch[13-15]是相等的,while循环再进行一次判断 ch[4]和ch[16]是否相等,如果ch[4]和ch[16]相等,那么 ch[17] 的最长公共前后缀长度是不是就变成 4 甚至超过 4 (这里我们当作是4)即ch[1-4]和ch[13-16]是相等的,next[17]=最长公共前后缀长度+1=4+1=5

在这里插入图片描述


如果还是不相等,再继续按照 j=next[j] 向前搜索直到找到新的最长公共前后缀或者搜索到 j=0即第一个位置。重复上述过程,这里放个图


在这里插入图片描述


我这个脑子你打死我也想不出这行代码是这样用的,不得不说大佬还是大佬,真的太强了!是不是感觉到这里就结束了?其实这个代码还可以再改进,详情请跳转另一位大哥的文章。
next数组的修正——nextval数组:https://blog.csdn.net/shn111/article/details/115540902

例如模式"aaaab"在和主串"aaabaaaab"匹配的过程中,当i=4,j=4时,s[4]≠t[4],根据next数组还需进行j=3,j=2,j=1这三次比较,实际上这三个字符与第四个字符都是相同的,因此不再需要比较,可以直接比较i=5,j=1。

索引 i12345
模式串aaaab
next01234
nextval00004

一行代码搞了我一个下午,虽然是有点搞心态,但是学到了东西,能够用自己的话表述出来还是挺有成就感的,以后遇到有意思的题还是得多留心一点🍅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值