KMP算法以及next函数值、nextval函数值的计算方法

KMP算法以及next函数值、nextval函数值的计算方法

数据结构中串涉及的内容即串的模式匹配,比较难理解的KMP算法,难在next函数值和nextval函数值的求解
一、问题描述
给定一个主串S及一个模式串P,判断模式串是否为主串的子串;若是,返回匹配的第一个元素的位置(序号从1开始),否则返回0;

先了解一下最简单直观的模式匹配算法BF(Brute-Force)算法,对理解KMP算法做个铺垫!

1、BF算法
假设S=“BBC ABCDAB ABCDABCDBADE”,P=“ABCDABD”。

  • 分别利用计数指针i和j指示主串S和模式串P中当前正在比较的字符位置,i的值可以指定,j初值为1;
  • S.ch[i]和P.ch[j]比较,若相等,则i和j分别指示串的下个位置,继续比较后续字符;
  • 若不相等,指针后退重新开始匹配,从主串的下一个字符(i=i-j+2)起再新和模式串的第一个字符(j=1)比较。

1、当S=“BBC ABCDAB ABCDABCDBADE”
与P="ABCDABD"进行匹配时:

  串S   BBC ABCDAB ABCDABCDBADE
  串P   ABCDABD

因为B与A不匹配,模式串后移一位。

  1. 直到文本串有一个字符与模式串中的第一个字符相同为止,然后逐一比较下一个字符,发现“空格”与“D”不匹配。

     BBC ABCDAB ABCDABCDBADE
         ABCDABD
    

3.重复第1第2步,能找到文本串中有“ABCDABD”与模式串相匹配,但效率很慢。
下面我们来了解另一种改进的模式匹配算法。

2、KMP算法
KMP算法,其改进在于,每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。

KMP算法是在已知模式串的next函数值的基础上执行的,那么,如何求得模式串的next函数值呢?(模式串根据next函数值向右“滑动”距离此函数值仅取决于模式串本身)

3、计算next函数值
【举例】
串“ababaaababaa”的next数组为

  • next[1]=0
  • next[2]=1

(这两个值是约定的)

  • next[3]:"ab"没有相同的前缀或后缀,所以模式串又得从头开始匹配,因此next[3]=1;
  • next[4]:“aba"的最长公共串是"a”,前三位匹配上了,并且第一位和第三位相同,因此可以将模式串整体向右移动,直到将原来的第一位移到原来的第三位上,继续进行匹配,而原来模式串指针指着的第四位现在指向第二位了,因此next[4]=2;
  • next[5]:“abab"的最长公共串是"ab”,用同样的方法得出next[5]=2;

以此类推······

  • next数组为:

0 1 1 2 3 4 2 2 3 4 5 6

4、计算nextval函数值
【举例】
串“ababaabab”的nextval为
在这里插入图片描述
总结如下:
1、nextval[1]和next[1]的值一样,为0;
2、如果s[i]==s[j],则nextval[i]=nextval[j];反之,nextva

  • 23
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
首先,KMP算法的next函数用于记录模式串中每个位置之前的子串中,最长的既是前缀又是后缀的长度。而nextval函数是在next函数的基础上,对于next[j]等于k的情况,如果模式串中的P[k]等于P[j],则nextval[j]等于nextval[k],否则nextval[j]等于k。 现在我们来求模式串abcdabcdab的next函数和nextval函数: 1. 计next函数: 首先,第一个字符是a,它之前没有子串,所以next[0]=-1。 接下来,我们从第二个字符b开始考虑。在它之前只有一个字符a,因此它的前缀和后缀都是空串,next[1]=0。 然后,我们考虑第三个字符c。它之前的子串是ab,前缀是a,后缀是b,二者不相等,所以next[2]=0。 接着,我们考虑第四个字符d。它之前的子串是abc,前缀是a、ab,后缀是c、bc,它们都不相等,所以next[3]=0。 接下来,我们考虑第五个字符a。它之前的子串是abcd,前缀是a、ab、abc,后缀是d、cd、bcd,它们都不相等,所以next[4]=0。 然后,我们考虑第六个字符b。它之前的子串是abcda,前缀是a、ab、abc、abcd,后缀是a、da、cda、bcda,它们都不相等,所以next[5]=0。 接着,我们考虑第七个字符c。它之前的子串是abcdab,前缀是a、ab、abc、abcd、abcda,后缀是b、ab、dab、cdab、bcdab,发现前缀ab和后缀ab相等,且长度为2,所以next[6]=2。 接下来,我们考虑第八个字符d。它之前的子串是abcdabc,前缀是a、ab、abc、abcd、abcda、abcdab,后缀是c、bc、dbc、cdbc、bcdc、abcdc,它们都不相等,所以next[7]=0。 然后,我们考虑第九个字符a。它之前的子串是abcdabcd,前缀是a、ab、abc、abcd、abcda、abcdab、abcdabc,后缀是d、cd、bcd、abcd、dabcd、cdabcd、bcdabcd,它们都不相等,所以next[8]=0。 最后,我们考虑最后一个字符b。它之前的子串是abcdabcda,前缀是a、ab、abc、abcd、abcda、abcdab、abcdabc、abcdabcd,后缀是a、ba、aba、daba、cdaba、bcda、abcda、dabcda,发现前缀aba和后缀aba相等,且长度为3,所以next[9]=3。 因此,模式串abcdabcdab的next函数为[-1, 0, 0, 0, 0, 0, 2, 0, 0, 3]。 2. 计nextval函数: 根据上面计出的next数组,我们可以得到nextval数组。 首先,nextval[0]=-1。 然后,我们计nextval[1]。由于next[1]=0,所以nextval[1]=0。 接着,我们计nextval[2]。由于next[2]=0,所以nextval[2]=0。 然后,我们计nextval[3]。由于next[3]=0,所以nextval[3]=0。 接下来,我们计nextval[4]。由于next[4]=0,所以nextval[4]=0。 然后,我们计nextval[5]。由于next[5]=0,所以nextval[5]=0。 接着,我们计nextval[6]。由于next[6]=2,且P[2]=P[6],所以nextval[6]=nextval[2]=0。 然后,我们计nextval[7]。由于next[7]=0,所以nextval[7]=0。 然后,我们计nextval[8]。由于next[8]=0,所以nextval[8]=0。 最后,我们计nextval[9]。由于next[9]=3,且P[3]=P[6],所以nextval[9]=nextval[3]=0。 因此,模式串abcdabcdab的nextval函数为[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值