关于KMP算法next数组的改进——nextval数组

写在最前:

KMP算法的核心思路:

问题由模式串决定,而不是由目标串决定!

此文前提是已掌握next数组算法!


问题引入:

假设模式串及其next数组:

模式串开始与目标串匹配:

模式串位置4与目标串位置4不匹配,所以下一步模式串指针应该按照next[4]移动:

但我们发现,这没有丝毫意义,刚刚就是因为模式串‘B’目标串‘C’不匹配导致我们按照数组next调整指针位置,但调整后的指针所在位置模式串元素仍为’B’,自然仍不匹配,还要继续按照数组next回溯指针。

但是我们可不可以改进next数组来避免做类似无用功呢?


分析与解决:

于是诞生了nextval数组,同样的,我们假设模式串为:

                    

**与next数组一致,如果位置1元素即不匹配,该位置nextval数组值为0**

(不是本文重要因素,故原因略去)

紧接着,假如位置1元素已匹配,位置2元素不匹配,按照位置2所对应的next数组值我们应回溯至位置1,但是我们发现位置1与位置2元素一样,所以说回溯之后仍是相同元素(非同一个)在与目标串比较,显然仍不匹配,就会继续回溯到位置1所对应的next数组值即0。

按照这个原理,我们设模式串的指针为k:

情况一:

特别地,我们发现位置1与位置2元素相同,在位置2不匹配(此时k=2)时我们按照next[2]=1,k回溯至位置1,像上文位置1元素与位置2元素相同,用模式串的位置1元素继续比较无意义,继续按照next[1]=0回溯指针,指针已到头,回溯结束,因此,我们记位置2对应的nextval数组值为0,此时next[2]=1而nextval[2]=0,两数组值不同。(记住这种情况的回溯过程,后文会引用)

情况二:

继续向下移动指针k,假设k移动到位置3,k之前元素已匹配,而k此时所指元素不匹配,那我们就指针回溯,按照位置3所对应的next数组值我们应回溯至位置2,这时我们发现情况和上述有所区别,位置2与位置3元素不相同,所以回溯后可能匹配成功(此时k=2),所以我们记位置3对应的nextval数组值为2,即nextval[3]=2=next[3],发现此时该位置next数组值与nextval数组值相同。

那何时next数组值与nextval数组值相同?又何时不同呢?

我们发现:当在k位置不匹配时,如果k位置所对元素与next[k]位置所对元素相同时,它们两数组值不同;而如果k位置所对元素与next[k]位置所对元素不相同时,它们两数组值相同

好,现在我们发现了这个规律,接着上面的情况,继续让k指针向下移动,k移动到位置4(之前元素已匹配),假设此时不匹配,指针回溯到next[4]=1,而位置1元素与位置4元素相同,继续回溯next[1]=0,所以位置4所对nextval数组值为0(nextval[4]=0),与next[4]=1不同,符合上述发现。

乘胜追击,继续让k指针向下移动,k移动到位置5,假设此时不匹配,指针回溯到next[5]=2,而位置2元素与位置5元素相同,继续回溯next[2]=1,位置1元素与位置5元素仍相同,继续回溯next[1]=0所以位置5所对nextval数组值为0(nextval[5]=0),与next[5]=2不同,符合上述发现。

但是,“乘胜追击”的情况指针k都不止回溯了一次,两次的回溯出现了两次的元素相同,在指针从2回溯到1回溯到最终的0的操作,实际上也就是当k=2(上文情况一)时指针最终回溯至nextval[2]=0,所以说我们可以直接利用之前(并不一定是上一次)操作得出的nextval数组值来简化重复的操作。也就是说,如果位置k的元素与next[k]元素相同时,nextval[k]=nextval[next[k]]

现在,我们已经验证并完善了“发现”关于“如果k位置所对元素与next[k]位置所对元素相同时,它们两数组值不同”,接着我们验证另一部分“如果k位置所对元素与next[k]位置所对元素不相同时,它们两数组值相同。”

继续下移指针k至位置7,假设之前元素已匹配,位置7此时不匹配,指针回溯至next[7]=4,位置4元素与位置7元素不同,此时位置4元素可能与目标串匹配成功,故nextval[7]=4,即nextval[7]=next[7]=4,太棒啦,“发现”的另一部分也被验证正确了。


结论:

如果位置k的元素与next[k]元素相同时,nextval[k]=nextval[next[k]]

如果位置k的元素与next[k]元素不同时,nextval[k]= next[k]


代码实现:

/*CaptainUniverse_ 2022.4.14*/
void get_nextval(String T,int *nextval)//T为模式串 
{
	j=0;//前缀 
	i=1;//后缀 
	nextval[1]=0;
	while(i<T[0])
	{
		if(j==0||S[i]==T[j])
	 	{
	 		i++;
	 		j++;
	 		if(T[i]!=T[j])
	 		{
	 			nextval[i]=j;
			}
			else
			{
				nextval[i]=nextval[j];
			}
		}
		else
		{
			j=nextval[j];
		}
	}
}
/*CaptainUniverse_ 2022.4.14*/

可略过部分:

 没错,文章的图全是用Excel做的。

你能体会那种感觉吗?就是你理解了某知识,

但是在脑中你却无法组织语言把它描述出来,

即使描述出来别人也很难理解你的语言。

我的辛酸,谁懂?

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值