深入理解kmp中的next数组
next数组
- 1. 如果对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相当于next[j] = k。
- 此意味着什么呢?究其本质,next[j] = k 代表p[j] 之前的模式串子串中,有长度为k 的相同前缀和后缀。有了这个next 数组,在KMP匹配中,当模式串中j 处的字符失配时,下一步用next[j]处的字符继续跟文本串匹配,相当于模式串向右移动j - next[j] 位。
举个例子,如下图,根据模式串“ABCDABD”的next 数组可知失配位置的字符D对应的next 值为2,代表字符D前有长度为2的相同前缀和后缀(这个相同的前缀后缀即为“AB”),失配后,模式串需要向右移动j - next [j] = 6 - 2 =4位。
向右移动4位后,模式串中的字符C继续跟文本串匹配。
- 2. 下面的问题是:已知next [0, ..., j],如何求出next [j + 1]呢?
对于P的前j+1个序列字符:
- 若p[k] == p[j],则next[j + 1 ] = next [j] + 1 = k + 1;
- 若p[k ] ≠ p[j],如果此时p[ next[k] ] == p[j ],则next[ j + 1 ] = next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 相当于在字符p[j+1]之前不存在长度为k+1的前缀"p0 p1, …, pk-1 pk"跟后缀“pj-k pj-k+1, …, pj-1 pj"相等,那么是否可能存在另一个值t+1 < k+1,使得长度更小的前缀 “p0 p1, …, pt-1 pt” 等于长度更小的后缀 “pj-t pj-t+1, …, pj-1 pj” 呢?如果存在,那么这个t+1 便是next[ j+1]的值,此相当于利用已经求得的next 数组(next [0, ..., k, ..., j])进行P串前缀跟P串后缀的匹配。
模式串的后缀:ABDE
模式串的前缀:ABC
前缀右移两位: ABC
读到此,有的读者可能又有疑问了,那能否举一个能在前缀中找到字符D的例子呢?OK,咱们便来看一个能在前缀中找到字符D的例子,如下图所示:
- void GetNext(char* p,int next[])
- {
- int pLen = strlen(p);
- next[0] = -1;
- int k = -1;
- int j = 0;
- while (j < pLen - 1)
- {
- //p[k]表示前缀,p[j]表示后缀
- if (k == -1 || p[j] == p[k])
- {
- ++k;
- ++j;
- next[j] = k;
- }
- else
- {
- k = next[k];
- }
- }
- }
用代码重新计算下“ABCDABD”的next 数组,以验证之前通过“最长相同前缀后缀长度值右移一位,然后初值赋为-1”得到的next 数组是否正确,计算结果如下表格所示:
从上述表格可以看出,无论是之前通过“最长相同前缀后缀长度值右移一位,然后初值赋为-1”得到的next 数组,还是之后通过代码递推计算求得的next 数组,结果是完全一致的。
3.3.5 基于《next 数组》匹配
下面,我们来基于next 数组进行匹配。
还是给定文本串“BBC ABCDAB ABCDABCDABDE”,和模式串“ABCDABD”,现在要拿模式串去跟文本串匹配,如下图所示:
在正式匹配之前,让我们来再次回顾下上文2.1节所述的KMP算法的匹配流程:
- “假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置
- 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
- 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
- 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[j],且此值大于等于1。”
- 1. 最开始匹配时
- P[0]跟S[0]匹配失败
- 所以执行“如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]”,所以j = -1,故转而执行“如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++”,得到i = 1,j = 0,即P[0]继续跟S[1]匹配。
- P[0]跟S[1]又失配,j再次等于-1,i、j继续自增,从而P[0]跟S[2]匹配。
- P[0]跟S[2]失配后,P[0]又跟S[3]匹配。
- P[0]跟S[3]再失配,直到P[0]跟S[4]匹配成功,开始执行此条指令的后半段:“如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++”。
- 2. P[1]跟S[5]匹配成功,P[2]跟S[6]也匹配成功, ...,直到当匹配到P[6]处的字符D时失配(即S[10] != P[6]),由于P[6]处的D对应的next 值为2,所以下一步用P[2]处的字符C继续跟S[10]匹配,相当于向右移动:j - next[j] = 6 - 2 =4 位。
- 3. 向右移动4位后,P[2]处的C再次失配,由于C对应的next值为0,所以下一步用P[0]处的字符继续跟S[10]匹配,相当于向右移动:j - next[j] = 2 - 0 = 2 位。
- 4. 移动两位之后,A 跟空格不匹配,模式串后移1 位。
- 5. P[6]处的D再次失配,因为P[6]对应的next值为2,故下一步用P[2]继续跟文本串匹配,相当于模式串向右移动 j - next[j] = 6 - 2 = 4 位。
- 6. 匹配成功,过程结束。
匹配过程一模一样。也从侧面佐证了,next 数组确实是只要将各个最大前缀后缀的公共元素的长度值右移一位,且把初值赋为-1 即可。
3.3.6 基于《最大长度表》与基于《next 数组》等价
我们已经知道,利用next 数组进行匹配失配时,模式串向右移动 j - next [ j ] 位,等价于已匹配字符数 - 失配字符的上一位字符所对应的最大长度值。原因是:
- j 从0开始计数,那么当数到失配字符时,j 的数值就是已匹配的字符数;
- 由于next 数组是由最大长度值表整体向右移动一位(且初值赋为-1)得到的,那么失配字符的上一位字符所对应的最大长度值,即为当前失配字符的next 值。
但为何本文不直接利用next 数组进行匹配呢?因为next 数组不好求,而一个字符串的前缀后缀的公共元素的最大长度值很容易求。例如若给定模式串“ababa”,要你快速口算出其next 数组,乍一看,每次求对应字符的next值时,还得把该字符排除之外,然后看该字符之前的字符串中有最大长度为多大的相同前缀后缀,此过程不够直接。而如果让你求其前缀后缀公共元素的最大长度,则很容易直接得出结果:0 0 1 2 3,如下表格所示:
然后这5个数字 全部整体右移一位,且初值赋为-1,即得到其next 数组:-1 0 0 1 2。
深入理解kmp中的next数组的更多相关文章
- 关于KMP中求next数组的思考【转】
文章转自 http://www.tuicool.com/articles/yayeIbe.这是我看到关于求next数组,解释最好的一篇文章!!!!!!! KMP的next数组求法是很不容易搞清楚的一部 ...
- Seek the Name, Seek the Fame---poj2752(kmp中的Next数组)
题目链接:http://poj.org/problem?id=2752 题意就是求出是已知s串的前缀的长度x,并且要求此前缀也是s串的后缀:求出所有的 x : Next[i]的含义是前i个元素的前缀和 ...
- POJ 2752 KMP中next数组的理解
感觉这里讲的挺好的.http://cavenkaka.iteye.com/blog/1569062 就是不断递归next数组.长度不断减小. 题意:给你一个串,如果这个串存在一个长度为n的前缀串,和长 ...
- KMP中next数组的理解
next数组是KMP的核心,但对于next数组我们总是有时候感觉明白了,但有时候又感觉没明白,现在我就说下我自己对KMP中next数组的理解,首先next[i]上的数字的意义,next[i]表示的是当 ...
- dutacm.club_1087_Common Substrings_(KMP)_(结合此题通俗理解kmp的next数组)
1087: Common Substrings Time Limit:3000/1000 MS (Java/Others) Memory Limit:163840/131072 KB (Java/ ...
- KMP 算法中的 next 数组
KMP 算法中对 next 数组的理解 next 数组的意义 此处 next[j] = k:则有 k 前面的浅蓝色区域和 j 前面的浅蓝色区域相同: next[j] 表示当位置 j 的字符串与主串不匹 ...
- poj 2406:Power Strings(KMP算法,next[]数组的理解)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 30069 Accepted: 12553 D ...
- POJ 2752 KMP中next数组的应用
题意: 让你从小到大输出给的字符串中既是前缀又是后缀的子串的长度. 思路: 先要了解这个东西: KMP中next数组表示的含义:记录着字符串匹配过程中失配情况下可以向前多跳几个字符,它描述的也是子串的 ...
- KMP算法中求next数组的实质
在串匹配模式中,KMP算法较蛮力法是高效的算法,我觉得其中最重要的一点就是求next数组: 看了很多资料才弄明白求next数组是怎么求的,我发现我的忘性真的比记性大很多,每次看到KMP算法求next数 ...
随机推荐
- callback res.end 记得return(Javascript需要养成的良好习惯)
错误示例: app.get('do',function(req,res,next){ getUserId(function(err,userId){ if(err){ res.end(err);//错 ...
- ssh 整合
1. 加入 Spring 1). 加入 jar 包 2). 配置 web.xml 文件 3). 加入 Spring 的配置文件. 2. 加入 Hibernate 1). 同时建立持久化类, 和其对应的 ...
- CSS之transition过渡练习
代码: <!DOCTYPE html><html><head> <title>transition</title> <meta cha ...
- Xcode 7如何 免费 真机调试iOS应用
运行Xcode后,点击菜单中的Preferences…进入Accounts标签,这里选择添加Apple ID: 在弹出的对话框中登入你的Apple ID,没有的话去注册一个就是了,登录成功后会看到下面 ...
- putty连接报NetWork error:connection refused
首先通过物理终端进入到linux上,手工检查ssh发现没运行 /etc/init.d/sshd status 使用rpm -V 命令可检查到ssh的软件包正常 /rpm -V openssh-serv ...
- 关于spring中无法将service注入到servlet中的问题
首先,servlet是动态网页项目区别于普通的java项目的,是动态网页项目中web.xml主要配置文件管理的,而spring只能管理普通的pojo,而没办法直接注入,尽管你的注入方式和配置方式都没有 ...
- php实现函数重载
java..net等强类型预言中都有方法重载,但是PHP是弱类型语言,不能确定参数的类型, 而且如果php定义的方法接收一个参数,调用的时候传入多个也不会有问题,所以不能进行重载. 但是我们可以通过p ...
- 了解JavaWeb,一篇就够
把HTML.CSS.JSP.JS.JavaScript,JQuery,STRUTS,String,MVC,DOM 柔和起来,贯穿成一篇完整的内容,让读者明白JavaWeb的前前后后. 从Servlet ...
- php 队列
一.php中对共享内存,消息队列的操作 http://blog.csdn.net/haitun312366/article/details/8614797 二.PHP memcache 队列类 htt ...
- Kali安装小问题解决及一些设置
昨天按照完Kali linux之后,更改了镜像源, 然后运行了 apt-get update && apt-get dist-upgrade 发现要 等大约两个小时才能跟新完毕.. 于 ...