KMP中next和nextval算法简析

本文深入浅出地介绍了KMP算法中的next和nextval数组的计算方法。通过具体实例,详细解释了如何根据模式串计算这两个数组,帮助读者理解并掌握KMP算法的核心。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自豆瓣大神总结,感觉这个讲得简单易懂,所以无耻的当个搬运工,谢谢大神总结

数据结构中,在串的这节,其实难度不大,关键是模式匹配的问题,而在模式匹配中要数KMP算法的难度最大,而KMP的next&nextval的计算则成了学习的重中之重。面对好多学校的教程跳过不讲,而教材里的公式又难以理解,好多自学的同仁们往往在这上面费尽心思。现在把我的学习心得发出来共享,如有错误,还望不吝指正。呵呵~

T : 1 2 3 4 5 6 7

串 : a b c a b a a

next : 0 1 1 1 2 3 2

nextval: 0 1 1 0 1 3 2
以上面的为例,先说next。next值本身的含义即是当进行匹配的模式串发生失配后,失配的这一字符再次进行匹配时要与哪个字符再进行比较?我们可以理解为这次配不上,那我下次和谁比?这样,next的值就与T发生了关系,表示了字符的比较位置。T就是元素的位置。
开始不用说,a前面没有,就是0,b要和a比,a的位置是1,b就是1。简单说,开头两个肯定是0,1!
第三个,c,c的前面是b,bc无法与开头的a比,所以c还是要单独找位于1的a,所以它也是1。
第四个,a,和第三个c一样,没有可以满足ca的字符串,最后a只能自己和第一个a配,它也是1。
第五个,b,看b前面,正好,a可以与开头的a对上,而第五个b正好在2的位置,所以b就是2。
第六个,a,看a前面,发现,a前面的ab可以与开头的ab对上,对上后,第六个a在位置3,所以就是3。
第七个,a,看a前面,只有a能够与开头配上,而“baa”则不行,所以它只有为2。
由此可以看出,next的计算,实际上就是计算当前字符以及当前字符前面的字符与开头匹配成功后,当前字符的最终位置,如果不成,就只能单独找第1个,所以它的值只能为1。
再看nextval,nextval是建立在next的基础上,我自己是这样理解的:同样是匹配,如果匹配成功,则位于后面的字符的next继承匹配成功的字符的nextval值,如果不成,保持不变。
以上为例:
第二位置的b按next值与开头a比较,不符,nextval不变,为1。
第三位置的c同样与1位置的a比较,不同,不变还是为1。
第四位置的a按next值与开头a比较,相同,继承开头a的nextval值,0。
第五位置的b按next值与二位置的b比较,相同,继承b的nextval值,1。
第六位置的a按next值与三位置c比较,不同,保持不变,3。
第七位置的a按next值与二位置b比较,不同,保持不变,2。
以上就是我的理解,当然,这是完全抛开了算法的描述,按照单纯计算next&nextval的值。关于算法,还是要好好理解的。
下面给出一个长的,加深印象。
            123456789……
             abcaabbabcabaacbacba
next: 01112231234532211211
nextval: 01102130110532210210
### KMP算法中的PM、nextnextval概念及其区别与联系 #### 1. PM (Prefix-Match) 数组 PM数组的核心在于记录模式串中每一位之前的最长相等前后缀长度。具体来说,对于模式串 `p` 的第 `j` 位字符,PM[j] 表示的是从模式串开头到当前字符之前的部分中,能够完全匹配的最大公共前缀后缀的长度[^3]。 例如,在模式串 `ababaaababaa` 中: - 当前位置为第7个字符(a),其前面部分为 `ababaaa`。 - 这里可以找到最大公共前缀后缀为 `aba`,因此对应的PM值为3。 #### 2. Next 数组 Next数组实际上是对PM数组的一种调整形式。通常有两种方式来构建Next数组:一种是将PM数组整体向右移动一位并填充初始值为 `-1` 或者 `0`;另一种则是基于特定需求对整个数组加上固定偏移量如加1操作。 通过这种转换处理之后,Next数组主要用于指导当发生不匹配情况时应该如何回溯指针的位置以便继续查找可能存在的其他潜在匹配点而无需重新扫描已验证过的序列片段[^4]。 ```cpp void Make_next(const string& pattern, vector<int>& next){ int n = pattern.length(); next.resize(n); // 初始化第一个元素 next[0]=-1; if( n >=2 ){ next[1]=0; for(int i=2;i<n;++i){ int t = next[i-1]; while(t != -1 && pattern[t]!=pattern[i-1]){ t = next[t]; } next[i] = t+1 ; } } } ``` #### 3. NextVal 数组 为了进一步提升效率减少不必要的重复比较动作,提出了改进版即NextVal数组的概念。它不仅考虑到了相同子串的情况还兼顾了某些特殊情形下的额外优化逻辑[^4]。 简单而言,如果某个位置处既有相同的前驱又有后续,则可以直接跳过这些冗余项从而加快搜索速度: - 如果当前位置上的字母与其指向的那个位置上的字母不一样的话就保持原样; - 否则将其更新成那个更早些时候所指示的新地址直到满足条件为止或者到达起点(-1)[^1]. --- ### 联系与对比总结表 | 特性 | **PM Array** | **Next Array** | **NextVal Array** | |------------|--------------------------------------|-------------------------------------|------------------------------------| | 定义 | 记录最长相等前后缀长度 | 基于PM调整用于实际匹配过程 | 在Next基础上做进一步剪枝优化 | | 初始状态 | 不一定 | 可能会设置为-1或0 | 继承自Next | | 功能差异 | 提供基础信息 | 实现基本功能支持 | 更高效地避免无意义比较 | ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值