KMP算法的一些理解

KMP算法可能是大家接触的第一个难以理解且高度抽象的算法了,笔者在最开始的学习里也一直都深受其困扰,难以窥见门道。但是经过一个下午的思考后,终于理解了其内涵。

这里需要推荐一篇我认为讲解的非常棒的博客,正是在它的启迪下,我对KMP才能迅速上手。

https://blog.csdn.net/weixin_46007276/article/details/104372119

一下是我个人总结的一些笔记,希望能对大家有所帮助。

KMP算法:

核心是next数组的计算

实际算法只需要在bp算法里将j回溯的位置调整为next【j】即可

 

理解next数组(手动写出next数组)

1.前后缀概念(看该字符前的字符串,从首尾两端开始,正序取不同数目的字符串比对,匹配上的最长字符数即为next值)

2.初始化,默认第一个元素的值为-1,第二个为0(next值)。

3.之后进行比较得到对应的next值,若完全失配取0(回溯到第一个位置),之后依次类推(配对长度为1回溯到1)。

 

(以上是最经典的模式,适用于下标从0开始存储字符串,且第一个元素初始化next为-1,第二个为0)

(在头歌版本,0下标不放元素,从1开始,且第一个元素next赋值为0,第二个为1,完全失配取1与经典模式区别在于头两个元素的next赋值与失配取值

(注意,无论是哪个版本,第一个元素都是特殊处理而非正常回溯,因为-10在对应位置并没有可以回溯的值,在主实现KMP的函数中会有所体现)

 

next函数编写

初始化k=0,j=1;(或者经典模式里k=-1,j=0开始)

第一个条件是(k=0(特殊处理)或者匹配情况下(j位置与k位置字符元素同))情况下

j,k同步后移(没失配情况下k始终指向j后面一个位置)

若不满足条件(未回溯到k=0或失配)

k=next【k】(将k再次回溯到前面,再次进行字符比对,j始终不变,不匹配层层往里回溯,若一直匹配到最后也失配则执行k=0的特殊化处理,j向后移动一位)

注意,nextj】的赋值与j的移动仅仅在第一个条件里完成,也就是说j移动的条件是两种:1是往前回溯的过程中找到了匹配的字符,重新进入条件中完成赋值。 2.是回溯到最底层,此刻的k取特殊化处理的值(0-1),然后重新进入,next值更新为k+1,然后j指针继续移动到下一个字符进行next值的赋值

 

nextval函数(next函数的改进)

只有一小步改动:在进入第一个条件内,j,k完成后移后,需要判断一下T【j】与T【k】的值是否相同(若相等代表前后两个比较配对的字符相等,此刻的next【j】没必要更新成k(因为两个字符相同,而可以更新为next【k】),这样会节省了连续相等字符带来的重复遍历)

步骤:T[j]T[k]是否相等,相等,nextvalj=nextvalk

否则 nextval【j】=k(这一步是next求解的正常操作)

int Get_Nextval(SString &T,int nextval[])
{
	int j=1,k=0;
	nextval[1]=0;
	while(j<T.length )
	{
		if(k==0 || T.ch[j]==T.ch[k])
		{
			j++;
			k++;
			if(T.ch[j]!=T.ch[k])
			nextval[j]=k;
			else
			nextval[j]=nextval[k];
		}
		else
		{
			k=nextval[k];
		}
	}
}

(因为我们先移动j指针,即进行j++操作后才进行nextval【j】的赋值,所以遍历结束的条件就是j<T.length(不是<=),这一点容易出错)

int Index_KMP(SString &S,SString &T,int nextval[])
{
	int i=1;
	int j=0;
	while(i<S.length && j<T.length)
	{
		if(S.ch[i]==T.ch[j] || j==0)
		{
			i++;
			j++;
		}
		else
		j=nextval[j];
	}
	if(j>=T.length)
	return (i-T.length);
	if(i>=S.length)
	return 0;
}

对于KMP主函数而言,我们要注意i,j代表的是数组的index下标,所以终止条件一定是<length。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向前进吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值