kmp算法中next向量数组的编写与优化

next数组是在kmp算法中用于目标字符串回溯时使用的,类似于给目标字符串中每个字符一个特征值,方便回溯,而不是每次都只是单纯的向后移动一位的暴力对比,可以极大的降低时间消耗。

我们首先要理解next数组是如何产生的

以”abcdaabcab“为目标“abcddabcababcdaabcababcdaabcabaa”为主串为例,当为如下情况:

abcddabcababcdaabcababcdaabcabaa                                                                                abcdaabcab

在第五个字符,也就是i=j=4时匹配出错那么我们就看j=4时对应的next值,然后让j=next[j],这个时候再用此时的j所对应的字符开始去与i=4时的字符继续比较(干说不好理解,先来点实际的)

预先告诉abcdaabcab这个东西对应的next为-1 0 0 0 0 1 1 2 3 1 (别问我咋来的这么快,一会儿教)那么j=next[j]=0,那么我们就直接吧编号为零的直接拽过来就行了

abcddabcababcdaabcababcdaabcabaa                                                                                                          abcdaabcab

前面几个都不用看的,看了也是无用功,总之就是这么用的

然后我们来说一说这个next是咋来的,其实很简单,就是看当前字符前面的字符串首尾有几个相同顺序的(用人话说不清楚)

abcdaabcab 这个红a,前面是abcd 首尾没有相同的所以这个a对应的next为0

abcdaabcab再看这个a,前面是abcd俩a首尾照应所以这个a对应的next为1

abcdaabcab看这个c,前面是abcdaab ab两字符首尾照应,所以这个c对应的next为2

同理 ,abcdaabcab,这个a对应的就是3啦(abcdaabcab)

注意:所找首尾不能重合!!!(比如cbcbca  a对应的next为1,而不是3)

这个其实就是找当前这个字符前有几个和开头时是一样的,这样的情况把开头的位置直接移过来也会有最大的可能性匹配成功,毕竟就是在这个位置才不一样的,比如:

在“abcddabcababcdaabccaabcababcabaa”中找“abcdaabcab“(注意主串和上面说的不一样)

有一种情况

abcddabcababcdaabccaabcababcabaa                                                                                                        abcdaabcab                              此时在标红处不同,红a对应的next为3那就把序号                               abcdaabcab                    为3的拽过来,只用一步这不就匹配对了嘛

根据这个思路我们就可以写出初始版的getnext函数

不优化的next数组

int* getnext(string aim,int next[])
{
    int k=-1;
    int j=0;
    next[0]=-1;//规矩 方便后面k与j的逐渐分离进行比较,赋值(记着就好)
    while (j < aim.size()) //这个很容易理解作为目标字符串的指针肯定不能跑出去
	{
		if (k==-1||aim[j] == aim[k])
		{
            j++;
		    k++;//肯定要+1,不然aim[k]就没意义了
			next[j] = k;
		}
		else
		{
			k = next[k];
		}
	}
}

这里比较好理解,k其实就是负责记录首尾有几个相同字符,j就是记录next数组记到哪了,观察可以发现j是只加不减,而k会因为k=next[k]一战回到解放前的,(具体可以自己一步一步手写去记录每一步j与k的值的变化,然后再改变目标串个别字符,比如把abcdaabcab中倒数第二个a换成d或者倒数第三个c换成b,去注意k的变化,自有一番天机)

缺点 还会有一定程度的无效配对,比如

abcddabcababcdaaccababcdaabcabaa                                                                                                                      abcdaabcab                             当这种情况时,因为b的next是1对应的依然是b,移动相当于有一定的浪费,所以可以进行一定的优化,保证都是有效移动

优化后的getnext

int* getnext(string mubiao,  int next[])
{

	int k = -1;
	int j = 0;
	next[0] = k;
	while (j < mubiao.size())
	{
		while (k >= 0 && mubiao[j] != mubiao[k])
			k = next[k];  //k的意义有所不同,一直保持的是重复段的next
		j++;
		k++;
		if (mubiao[j] == mubiao[k])
		{
			next[j] = next[k];  
		}
		else
		{
			next[j] = k;
		}
	}
}

对于字符串”abcdaabcab“

在优化后的程序中运行

每一步进行的数据变化

k=-1 j=0 next[j]=-1 0 1 next[1]=0 -1 0 2 next[2]=0 -1 0 3 next[3]=0 -1 0 4 next[4]=-1 1 5 next[5]=k=1 0 1 6 next[6]=next[1]=0 2 7 next[7]=next[2]=0 3 8 next[8]=k=3 0 1 9 next]9]=next[1]=0

优化后的程序对于字符串”abcdaabcab“便可以利用k来记录当前位置以及前面的位置是否与开头部分相同,从而确定是使next[j]=k 还是next[k],等于next[k]其实就是保证有效的移动

(最后一点优化部分写的不是很好,后面我会继续完善,也请大神指点)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值