KMP算法中next函数的理解
首先要感谢http://blog.csdn.net/v_july_v/article/details/7041827以及http://blog.chinaunix.net/uid-27164517-id-3280128.html两篇博文的作者,参考这两篇博文才对KMP算法有了初步认识,本文的一些内容也是来自于这两篇之中。KMP算法与BF算法的优略、回溯不回溯这些问题本文不作说明,而主要说明next函数(通常保存为一个next数组)的意义。这正是KMP算法难于理解的地方。
为了方便起见,在不会起歧义的情况下做如下约定:下标都从0开始; 假设字符串为S,那么Si 表示第i个字符或者只有第i个字符的字符串;SiSi+1Si+2表示子串,如S1S2S3,S4S5等等;pre(S,i)表示字符串S的位置i之前的字符串,即S0S1...Si-1;next函数和next数组表示同一个意思。本文分为两节,第一节讲next函数的意义,得出需要满足的两个条件;第二节是具体代码以及相关说明。
第一节 next函数的意义
我们知道,KMP的基本匹配过程如下:在字符串T中查找模式P,需要记录T中的当前位置i 以及P中的当前位置 j。当Ti=Pj的时候i和j都自增;当Ti!=Pj时,令j=next(j),然后继续匹配。这样就跳过了一些字符,而这些字符,本质上来讲与字符串P0P1...Pj-1的前缀和后缀能匹配的最大长度相关。以图一作解释,来倒推next(j)的意义:

图 1
在T中查找P,P0P1P2P3P4=T1T2T3T4T5,但P5!=T6(i=6,j=5)。此时令j=next(j)。假设next[5]=2, 则跳过三个字符,新的j=2,从这个位置开始比较。能跳过的条件是什么?其一,从虚线部分可知必须保证P0P1=P3P4;其二,P5不能等于P2,因为之前我们知道了P5!=T6,如果P5=P2,那么P2肯定不等于T6。再次强调观察虚线部分,发现P0P1和P3P4正好是字符P5前面的字符串(即P0P1P2P3P4)的前缀和后缀。
图1中,P5!=T6时跳过了三个字符,next[5]=2。再看图2:next(5)分别等于4,3,1,0的情况。

图 2
仔细观察图2中的四种情况,均需要符合上面所说的条件。对于图2的最后一幅图,此种情况的条件是P0P1P2P3P4没有相等的前缀和后缀,且P0!=P5。那么如果后一个条件不满足呢,那么显然P应该再移一个位置,对应的情况如图3:

图 3
图3中,next(5)=-1。因此next=-1的情况:Pj=P0,且P0P1...Pj-1没有任何相等的前缀和后缀。另外,一般地,如果P0就发生失配,那么显然i也要加一,因此next(0)=-1。
在此作一小结,k=next(j)需要满足的两个条件如下:
条件1. k是P0P1...Pj-1最长匹配的前缀和后缀的长度.
条件2. Pj!=Pk.
第二节 next函数的求法
利用以上知识,我们就知道求next函数的思路了。基本思路是利用上面的第一个条件(寻找最长匹配的前后缀),而第二个条件(Pj!=Pnext(j))则作为优化。这样一步步理解会对算法思路更清晰一点。
基本思路: 利用条件1
使用归纳法:假设next(j)=k,则P0P1...Pk-1=Pj-k...Pj-2Pj-1,那么next(j+1)有两种情况:
1. 如果Pk=Pj,则P0P1...Pk=Pj-k...Pj-1Pj,所以next(j+1)=k+1=next(j)+1。
2. 如果Pk!=Pj,这是可以看做另外一个字符串匹配的问题,主串和模式串都是p,当匹配失败时,k=next(k)。
因此得到如下算法:
void get_nextval(char const* ptrn, int plen, int* nextval)
{
int i = 0;
nextval[i] = -1;
int j = -1;
while( i < plen-1 )
{
if( j == -1 || ptrn[i] == ptrn[j] ) //对应情况1
{
++i;
++j;
next(i)=j;
}
else //对应情况2
j = nextval[j];
}
}
上述算法中,ptrn是模式串,plen是模式串长度,nextval数组保存所有位置的next值。该算法不考虑条件2,因此有可能发生Pi=Pnext(i)这种情况。在缺少该条件的情况下也可以用于做字符串匹配。假设next(i)=k,当匹配到i失效时,i=next(i)=k,这时候肯定也失效,因此又寻找k对应的next值,这样算法得以进行。
优化:利用条件2
比较常见的算法对情况1做了优化,如下:
void get_nextval(char const* ptrn, int plen, int* nextval)
{
int i = ;
nextval[i] = -;
int j = -;
while( i < plen- )
{
if( j == - || ptrn[i] == ptrn[j] ) //对应情况1
{
++i;
++j;
if( ptrn[i] != ptrn[j] )
nextval[i] = j;
else
nextval[i] = nextval[j];
}
else //对应情况2
j = nextval[j];
}
}
该版本的算法考虑了条件2,因此进入情况1的时候,next(i)!=j。我们可以考虑一条查询链,如图4:

图4
假设现在刚刚运行完13行,得出next[i]=j。此时必然有ptrn[i]!=ptrn[j]。因此下个循环的时候会跳转到18行。该next链一直往前搜寻,直到某个位置k,ptrn[k]与ptrn[i]相等。该k就是最新的j值,这样回到情况1,接着按照条件1优化。另外,当j==-1也应当进入情况1,因为不能往前搜寻了。
以上就是next数组的求解过程,往后就可以利用next数组进行字符串查找了。在写查找算法的过程中,可以发现与求next数组的算法过程惊人的一致。这也是KMP算法的一个特点,把两者结合起来,更能够理解它的奥妙所在。
KMP算法中next函数的理解的更多相关文章
- KMP算法中next数组的理解与算法的实现(java语言)
KMP 算法我们有写好的函数帮我们计算 Next 数组的值和 Nextval 数组的值,但是如果是考试,那就只能自己来手算这两个数组了,这里分享一下我的计算方法吧. 计算前缀 Next[i] 的值: ...
- KMP算法中我对获取next数组的理解
之前在学KMP算法时一直理解不了获取next数组的函数是如何实现的,现在大概知道怎么一回事了,记录一下我对获取next数组的理解. KMP算法实现的原理就不再赘述了,先上KMP代码: 1 void g ...
- KMP算法的next函数求解和分析过程
转自 wang0606120221:http://blog.csdn.net/wang0606120221/article/details/7402688 假设KMP算法中的模式串为P,主串为S,那么 ...
- 问题 1690: 算法4-7:KMP算法中的模式串移动数组
题目链接:https://www.dotcpp.com/oj/problem1690.html 题目描述 字符串的子串定位称为模式匹配,模式匹配可以有多种方法.简单的算法可以使用两重嵌套循环,时间复杂 ...
- KMP 算法中的 next 数组
KMP 算法中对 next 数组的理解 next 数组的意义 此处 next[j] = k:则有 k 前面的浅蓝色区域和 j 前面的浅蓝色区域相同: next[j] 表示当位置 j 的字符串与主串不匹 ...
- 关于KMP算法中,获取next数组算法的理解
参考:KMP入门级别算法详解--终于解决了(next数组详解) https://blog.csdn.net/lee18254290736/article/details/77278769 在这里讨论的 ...
- KMP算法中求next数组的实质
在串匹配模式中,KMP算法较蛮力法是高效的算法,我觉得其中最重要的一点就是求next数组: 看了很多资料才弄明白求next数组是怎么求的,我发现我的忘性真的比记性大很多,每次看到KMP算法求next数 ...
- KMP算法中的几个疑问
KMP算法next数组求解实现 首先我们通过应用场景将KMP算法中用到的名词做一个说明: 在一个字符串(string1)中查询是否存在另一个字符串(string2). 在字符串匹配算法中,我们通常将字 ...
- poj 2406:Power Strings(KMP算法,next[]数组的理解)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 30069 Accepted: 12553 D ...
随机推荐
- SonarLint插件的安装与使用
注意:版本要求Eclipse(4.2,3.8)以上,Java3.1.2,JavaScript 2. 一.SonarLint插件的安装方式 1.安装方式一:在线安装 1)Eclipse工具栏选择Help ...
- Illegal mix of collations (big5_chinese_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation 'like'
解释: 非法的混合排序规则(big5_chinese_ci)和(utf8_general_ci)操作“like”. 原本是单个字段查询数据的,现在是把所有的字段用一个搜索框来查询. 主要出问题是下列这 ...
- Mybatis调用Mysql存储过程
在我的后台系统中,今天需要使用到存储过程.存储过程还真没写过,今天就写了个存储过程.使用在后台中. 其实这个接口功能 是涉及几张表的修改,删除,新增的.就写个一个存储过程. 存储过程: ), ),) ...
- C#_抓包HttpWebRequest跟HttpWebResponse
1.第一招,根据URL地址获取网页信息 这招是入门第一式, 特点: 1.最简单最直观的一种,入门课程. 2.适应于明文,无需登录,无需任何验证就可以进入的页面. 3.获取的数据类型为HTML文档. ...
- HBase的shell命令行界面按退格键(Backspace)无法删除问题
在HBase的shell命令行界面输入错误项按"退格键"删除,却怎么也删除不了: 解决办法: 第一步,修改SecureCRT的设置参数: 第二步,按"Ctrl+退格键(B ...
- SQL Server常见基础操作
1. 常见针对表的操作(增删改查) --1. Create Table USE [MVC_000] CREATE TABLE T_TableName ( ID ,) PRIMARY KEY, Name ...
- 利用ADO.NET导出大批量数据
2015年12月,XX项目中需要做一个数据导出功能,当时所有页面的到处功能均已经实现,但有个页面数据量太大,导出过程中导出页面直接卡死.不得已我准备选用ADO.NET来重新完成这个功能,因为考虑到越偏 ...
- 检测访问网页的浏览器呈现引擎、平台、Windows操作系统、移动设备和游戏系统
/** * Author: laixiangran. * Created by laixiangran on 2015/12/02. * 检测访问网页的浏览器呈现引擎.平台.Windows操作系统.移 ...
- POJ 1218
题目描述看着就乐了,死板得只按着题意来写了ps: tequi是escape的方言版.. #include <iostream> using namespace std; int main( ...
- WebSocket 浅析
版权声明:本文由史燕飞原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/241 来源:腾云阁 https://www.qclo ...