KMP算法中NEXT数组的作用以及代码实现
在http://blog.csdn.net/u012613903/article/details/79004094中写到了如何手工去求一个NEXT数组,这个在很多考试中可以用来解题。但是在实际的使用中,NEXT数组究竟发挥着什么样的作用,如何用代码实现KMP算法呢?
KMP算法是用来确定一个串是否能在另一个串中找到与之完全匹配的子串,那么首先来看一个字符串匹配的实际例子;
被匹配的字符串:
a | b | c | d | a | b | e | f |
要匹配的字符串:
a | b | c | d | a | b | w |
下面开始进行匹配:(i代表被匹配的字符串的当前字符下标;j代表要匹配的字符串的当前字符下标。)
0 | 1 | 2 | 3 | 4 | 5 | i | |
a | b | c | d | a | b | e | f |
a | b | c | d | a | b | w | |
0 | 1 | 2 | 3 | 4 | 5 | j |
可以看到,在{e,w}的位置匹配失败了,如果使用暴力的方法做,那么下一次的匹配就变成了这个样子:
0 | i | 2 | 3 | 4 | 5 | 6 | 7 |
a | b | c | d | a | b | e | f |
a | b | c | d | a | b | w | |
j | 1 | 2 | 3 | 4 | 5 | 6 |
这时候 i = i-j+1;j=0;这就是暴力算法的思想:一旦匹配失败,就让要匹配的字符串从头开始与被匹配的字符串进行匹配。
我们来观察第一次匹配失败后的情况:
0 | 1 | 2 | 3 | 4 | 5 | i | |
a | b | c | d | a | b | e | f |
a | b | c | d | a | b | w | |
0 | 1 | 2 | 3 | 4 | 5 | j |
在要匹配的字符串的{w}位置,往前看,看到♦的a,b与实心♦的a,b就是我们要求的最长的相同前缀与后缀字符串
其实它们代表的实际意义是这样的:
在{e,w}位置无法进行匹配了,但是在w之前的要匹配的字符串的所有字符都与在e之前的被匹配的字符串的所有字符完全匹配了;(这是个前提标记为@1)
下面具体分析实际情况:
a | b | c | d | a | b | e | f |
a | b | c | d | a | b | w |
在{e,w}位置匹配失败了,我们不想使用暴力方法;想省力,那该怎么办呢?
要匹配的串中的w和被匹配串的e不匹配,进行下次匹配的时候,必然会有要匹配串中的w之前的某个字符取代w。也就是要将要匹配的串向右平移一个量(j+这个量<=i)。这里我们列出所有的平移情况:
(1)
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w |
(2)
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w |
(3)
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w |
(4)
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(5)
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w |
(6)
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w |
上面就是所有的平移情况,情况(1),(2),(3)从第一个就不匹配;情况(4)从第三个不匹配;(5),(6)不需要讨论;可以看到,在(4)之前都是从第一个字符起两个串就不匹配,直到(4)才出现不是在第一个字符就不匹配的情况。然后再看(4)中的匹配上的字符串,居然是 a,b,是要匹配的 最长的相同前缀与后缀字符串。为什么会这样,这就用到了上面的前提@1 。
再回到最初不匹配的情形下:
a | b | c | d | a | b | e | f |
a | b | c | d | a | b | w |
正如前提@1所说,e,w之前两个字符串完全匹配,也就是完全相同。所以要求被匹配串e之前与要匹配串从0开始相同的最长子串串也就变成了在要匹配字串中求w之前与要匹配串中从0开始相同的最长子串
a | b | c | d | a | b | e | f |
a | b | c | d | a | b | w |
就像图中表示的那样,要求黄色与红色的关系,就是求绿色与红色的关系;所以我们在求NEXT数组时所求出的最长的相同前缀与后缀字符串就是在不匹配的位置之前被匹配字符串中与要匹配的字符串中从0开始所以字符完全相同的最长的子串。
然后(1)->(2)->(3)->(4)的平移过程,其实就是使用暴力法的过程;KMP算法就是利用了这个特性,在某个位置发生不匹配的情况后,直接将比较情况变成(4)跳过了(1)、(2)、(3)这些从一开始就不匹配的匹配情况,而且断定了在a,b已经匹配,只需要测试a,b之后的字符是否匹配就可以了,如下图所示:
0 | 1 | 2 | 3 | 4 | 5 | i | 7 | ||||||||
a | b | c | d | a | b | e | f | ||||||||
a | b | c | d | a | b | w | |||||||||
0 | 1 | j | 3 |
在i的位置发生了不匹配,直接平移到了上图中的情况,然后直接从j=2位置的要匹配字符串的字符与i=6位置的被匹配字符串的字符进行对比就可以了;
为什么能跳过暴力算法的从开始就不匹配的步骤,并且确定了这次比较中已经匹配的字符串的长度呢?
个人理解:在i位置发生了不匹配的情况,我们从i往前逐渐加大长度来取得字符串,然后确定这个字符串在要匹配的串中从0开始能否得到,知道走到得不到的时候,
也就是恰好如上图中的 a,b,c中的c与 d,a,b中的b不想同的时候,由于d的出现,就可以确定它们从一开始就无法匹配;而反之取到 a,b的时候,说明a,b是可以匹配的,只需要确定i以及i之后与a,b之后是否能够匹配就可以了。
最后确定一下j与NEXT数组的关系,求出要匹配的字符串的NEXT数组的值,如下:
a | b | c | d | a | b | w |
0 | 1 | 1 | 1 | 1 | 2 | 3 |
在这里,j=NEXT值-1; 其中当NEXT为0的时候,j=-1;这种情况说明第一个字符就不匹配,在算法中i会直接+1,然后j也直接+1;也就是从被匹配串的第i+1位置和要匹配串从0开始进行匹配。
下面是算法的具体实现,(最近在学python,所以就用python写了;PS:求next数组不是最优化的算法)
# -*- coding:utf-8 -*-
# Author: Evan Midef kmp(haystack, needle):slen = len(haystack)plen = len(needle)nexts = [0] * plenget_next(needle, nexts)print(nexts)i = 0j = 0
"""
当haystack[i] == needle[j]的时候,直接i++,j++看下一个是否匹配
一旦不匹配那么i不变,j=nexts[j]-1;边界情况下,j=nexts[0]-1=-1;
这中情况说明没有满足的NEXT值,也就是在i前面的已匹配字符串的长度为-1
也就是在i+1前面已匹配的字符串的长度为0(即从i+1与j=0处开始比较),
恰好也对应i++ (=>i+1),j++( =>0)然后看下一个是否匹配
"""while i < slen and j < plen:if haystack[i] == needle[j] or j == -1:i += 1j += 1else:j = nexts[j]-1if j == plen:return i-jelse:return -1"""
设要求的字符串为A
在求next数组的时候我们已经求的了位置j的NEXT值为k+1
也就是说在要求的字符串中从[0到k-1]与[j-k到j-1]完全相同,
所以如果A[k]=A[j]的时候,只需要j++;k++;nexts[j]=k+1 就求的了j+1位置的NEXT值
当A[k]!=A[j]的时候,我们把A进行复制,赋值一个A';让k对应与A',让j对应于A;且容易得到A'的NEXT数组和A完全相同
就可以看作在A'中[0,k-1]与A中[j-k,j-1]完全相同(也就是匹配成功),但是在{j,k}处匹配失败,所以j不变,而是将
k赋值为next[k]-1(在k之前的所有NEXT值是已经求得的),最差的情况k会取到NEXT[0]-1也就是k=-1;这时在j+1之前的A'
的长度是k+1=0;所以A[j+1]要与A'[0]对应,也就是k=0;所以也需要执行操作j++ ( =>j+1),k++( =>0) 然后nexts[j]=k+1
"""def get_next(needle, nexts):al = len(needle) #求出要求NEXT数组的字符串的长度nexts[0] = 0 #NEXT[0]是恒定值0k = -1j = 0while j < al - 1: if k == -1 or needle[j] == needle[k]:j += 1k += 1nexts[j] = k+1else:k = nexts[k]-1print(kmp("hello", "ll"))
KMP算法中NEXT数组的作用以及代码实现相关推荐
- 数据结构与算法之KMP算法中Next数组代码原理分析
2019独角兽企业重金招聘Python工程师标准>>> 一.KMP算法之Next数组代码原理分析 1.Next数组定义 当模式匹配串T失配的时候,Next数组对应的元素指 ...
- KMP算法中next数组的理解与算法的实现(java语言)
KMP 算法我们有写好的函数帮我们计算 Next 数组的值和 Nextval 数组的值,但是如果是考试,那就只能自己来手算这两个数组了,这里分享一下我的计算方法吧. 计算前缀 Next[i] 的值: ...
- KMP算法中next数组到底有什么深意
要了解KMP首先我们回顾一下单纯暴力的BF算法 现有字符串S(a,b,a,b,a,b,f),模式串P(a,b,a,b,f),根据BF算法首先我们将子串S[1,5]与P进行匹配,若匹配失败则再进行子串S ...
- 字符串匹配KMP算法中Next[]数组和Nextval[]数组求法
数据结构课本上给了这么一段算法求nextval9[]数组 1 int get_nextval(SString T,int &nextval[ ]) 2 { 3 //求模式串T的next函数修正 ...
- 数据结构 KMP算法中next数组与nextval如何求
个人学习用,比较杂乱. 一.例题 二.题目(3)next解法见图 三. nextval如何求解? 3.1步骤一:画图,先求出next 3.2 位置1的数必定为0,记住即可 位置2的next为1,所以要 ...
- KMP算法 → 计算nextval数组
[算法解析] ● 众所周知,KMP算法中模式串T的next数组,是KMP算法的核心. next数组的核心作用是"当模式串T的第j位与主串S的第pos位失配时(即 T[j]≠S[pos] 时) ...
- 串的模式匹配、KMP算法、nextval数组求法
一.暴力匹配 #include <iostream> using namespace std; #define MAXLEN 255 typedef struct{char ch[MAXL ...
- c语言数据结构kmp中next计算,数据结构——关于KMP算法中next函数的详细解析
以前看到数据结构中字符串的模式匹配时,花了半天的时间,才把KMP算法中的next函数整明白了,结果过了几天在看到这时,只记得next[j+1]=next[j]+1,但是有时候能套公式正确算出,有时候就 ...
- KMP算法的next数组通俗解释
我们在一个母字符串中查找一个子字符串有很多方法.KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度. 当然我们可以看到这个算法针对的是子串有对称属性, ...
最新文章
- SLF4J: Failed to load class org.slf4j.impl.StaticLoggerBinder.
- 一个月6次泄露,为啥大家用Elasticsearch总不设密码?
- 【 C 】动态内存分配案例分析
- VS 2008 中内存泄露检查
- #脱贫攻坚#彭蕾卸任蚂蚁金服后首次现身:女性脱贫亮三招
- 小米做的这件事,捍卫了你的隐私
- 用开源项目PhotoView实现图片的双指缩放和双击放大缩小
- spring项目搭建云服务器,Spring Boot项目打包并部署到云服务器
- Codeigniter 3 拓展HMVC
- 节省服务器成本50%以上!独角兽完美日记电商系统容器化改造实践
- 你在寻觅冬季唯美的海报设计素材么?
- ExComboBoxColor 控件
- 從零開始學 ReactJS:最完整的 React 生態系入門教程
- java多行字符串变量_java – Scala – 如何在多行字符串文字中使用变量
- 抽象工厂模式_设计模式3之抽象工厂模式
- PHP开发环境phpnow的详细安装步骤
- 三、EXCEL复制数字到txt文件,存在空格
- 工作流系统之四十二 业务规则和业务过程
- GitHub个人Blog完全攻略
- 关于MediaCode播放H265/hevc的总结
热门文章
- oracle11g基目录和主目录,Red Hat Enterprise Linux4.0 安装oracle11g
- java字节流分为_Java文件流可分为字节流和字符流。
- redhat下的iptables和firewalld 笔记
- HTML+CSS+JS实现echarts图表炫光分布地图动画
- 如何将c语言程序封装供python调用_C++调用python
- java定义一个方法,返回一个整数数组的元素最小值
- java中main缺少主体_缺少方法主体,或声明了摘要
- Java 查看指定文件最后的修改时间
- 计算机基础 英文版本,计算机基础,全英文版的.ppt
- ado.net mysql 连接池_ADO.NET数据连接池