next数组

定义

  • 严格定义:next[i]表示使子串s[0...k] == s[i-k...i]的最大的k(前后缀可以重叠,但不能是s[0..i]本身)
  • 含义:最长相等前后缀的下标,没有则赋-1
  • 图形化解释:s[0]开始找到一个最长子串,满足一个条件:把该子串拉到末尾时能与母串的完全重合



求解

递归

上述判断可以归纳为一个递归过程:

读取两行子串,一行提供前缀,一行提供后缀。

读取新字符s[i]时,后缀行不断向左滑动。

如果能匹配,则根据后缀行最后一个匹配元素下标即next值

否则,后缀行向右滑动,直到找到一个完全匹配处

举例

如果已知next[0]~next[3],如何递归地求出next[4]和next[5]

求next[4]:

已知next[3]=1,由于s[4]==s[next[3]+1]所以最长相等前后缀拓展,next[4]=next[3]+1

如果令j=next[3]则上述两个式子变成s[4]=s[j+1],next[4]=j+1

求next[5]:

已知next[4]=2,s[5]!=s[j+1]此时最长相等前后缀无法拓展,需要将后缀串向右滑动 到某个位置,使之满足"s[5]==s[j+1]",如图12-3最右图

现在确定j:本质就是确定 ~

由于 ~ 是由"aba"向右滑动得来的,所以它是aba的前缀

由于 ~ 又是“aba”的后缀,如图12-3最右图,所以可知 ~ 是"aba"的最长相等前后缀

"aba"在后缀行的下标为0-2,所以 j = next[2] (结合next数组的定义再理解一下) = next[next[4]] = j'(计算next[4]时的j值)

所以求解next[5]时,只需令next[5]=next[2],再判断s[5] == s[j+1]是否成立

如果成立,next[5]=next[j]+1

否则,不断令j=next[j],直到j=-1或者途中s[5] == s[j+1]成立

实现

步骤

  1. 初始化next数组,next[0] = j = -1
  2. 令i由1-(len-1)重复 3. 4.
  3. 不断令 j = next[j], 知道 j !=-1 或者 s[i] == s[j+1],
  4. 如果 s[i] == s[j+1],next[i] = j+1

代码

//getNext求解长度为len的字符串s的next数组
void getNext(char s[], int len){
int j = -1;
next[0] = -1; //初始化 j = next[0] = -1
for(int i = 1; i < len; i++){
while(j != -1 && s[i] != s[j+1]){ //求解next[1] ~ next[len-1]
j = next[j]; //反复令j = next[j]
} //直到j回退到-1,或是 s[i] == s[j+1]
if(s[i] == s[j+1]){
j++; //则next[i] = j + 1,先令j指向这个位置
}
next[i] = j; //令next[i] = j
}
}

不难发现,j是用来给next[i]赋值以及在递归求解(代码中用循环代替了递归,但本质是递归思想)过程中给记录前一个next值的中间变量

KMP算法

分析

字符串匹配,被匹配串:文本串text,匹配串:模式串patten

初始化,令j = -1, i = 0。

如下图,遍历text,当text[i] == patten[j+1]时,i和j都不断右移

如下图,当出现 text[i] != patten[j+1]时, 需要将patten向右滑动,直到满足条件 text[i] == patten[j+1],

不难发现,这一过程和求解next数组时失配的情况非常类似,和求解next数组时一样的思路,只需要令j = next[j],就可以让patten快速移动到相应位置。可见,next[j]就是当前j失配时,j应该回退的位置。

最后如果 j == 5也匹配成功,说明patten是text的子串

实现

步骤

  1. 初始化j=1
  2. 让i遍历text数组,对每个i,执行3.4.来试图匹配text[i]和patten[j+1]
  3. 不断令 j = next[j],直到 j == -1或 text[i] == patten[j+1]
  4. 如果text[i] == patten[j+1], 令 j++; 当 j== m-1时说明patten是text子串

代码

//KMP算法,判断pattern数组是否是text的子串
/*O(m+n)*/
bool KMP(char text[], char patten[]){
int n = strlen(text), m = strlen(patten); //字符串长度
getNext(patten, m); //计算patten的next数组
int j = -1; //初始化j为-1,表示当前还没有任意一位被匹配
for(int i = 0; i < n; i++){ //试图匹配text[i]
while(j != -1 && text[i] != patten[j+1]){
j = next[j]; //不断回退,知道j回到-1 或 text[i] == patten[j+1]
}
if(text[i] == patten[j+1]){
j++; //text[i]与patten匹配成功,令j加1
}
if(j == m-1){
return true; //patten完全匹配,说明patten是text的子串
}
}
return false; //执行完text还没匹配成功,说明patten不是text的子串
}

完整代码

#include<stdio.h>
#include<string.h>
const int MaxLen = 100;
int next[MaxLen];
//getNext求解长度为len的字符串s的next数组
void getNext(char s[], int len){
int j = -1;
next[0] = -1; //初始化 j = next[0] = -1
for(int i = 1; i < len; i++){
while(j != -1 && s[i] != s[j+1]){ //求解next[1] ~ next[len-1]
j = next[j]; //反复令j = next[j]
} //直到j回退到-1,或是 s[i] == s[j+1]
if(s[i] == s[j+1]){
j++; //则next[i] = j + 1,先令j指向这个位置
}
next[i] = j; //令next[i] = j
}
} //KMP算法,判断pattern数组是否是text的子串
/*O(m+n)*/
bool KMP(char text[], char patten[]){
int n = strlen(text), m = strlen(patten); //字符串长度
getNext(patten, m); //计算patten的next数组
int j = -1; //初始化j为-1,表示当前还没有任意一位被匹配
for(int i = 0; i < n; i++){ //试图匹配text[i]
while(j != -1 && text[i] != patten[j+1]){
j = next[j]; //不断回退,知道j回到-1 或 text[i] == patten[j+1]
}
if(text[i] == patten[j+1]){
j++; //text[i]与patten匹配成功,令j加1
}
if(j == m-1){
return true; //patten完全匹配,说明patten是text的子串
}
}
return false; //执行完text还没匹配成功,说明patten不是text的子串
}

关系

求解nex数组的过程就是模式串patten自我匹配的过程

<数据结构>KMP算法的更多相关文章

  1. 数据结构--KMP算法总结

    数据结构—KMP KMP算法用于解决两个字符串匹配的问题,但更多的时候用到的是next数组的含义,用到next数组的时候,大多是题目跟前后缀有关的 . 首先介绍KMP算法:(假定next数组已经学会, ...

  2. 实验数据结构——KMP算法Test.ming

    翻译计划     小明初学者C++,它确定了四个算术.关系运算符.逻辑运算.颂值操作.输入输出.使用简单的选择和循环结构.但他的英语不是很好,记住太多的保留字,他利用汉语拼音的保留字,小屋C++,发明 ...

  3. 数据结构——KMP算法

    算法介绍 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法).KMP算法的核心是利用 ...

  4. 数据结构-kmp算法

    定义 改进字符串的匹配算法 关键:通过实现一个包含了模式串的局部匹配信息的next()函数,利用匹配失败的信息,减少匹配次数. 1.BF算法 暴力匹配 给定 文本串S "BBC ABCDAB ...

  5. 大话数据结构——KMP算法(还存在问题)

    http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html /*#include& ...

  6. 数据结构KMP算法中手算next数组

    总结一下今天的收获(以王道数据结构书上的为例子,虽然我没看它上面的...):其中竖着的一列值是模式串前缀和后缀最长公共前缀. 最后求得的结果符合书上的结果,如果是以-1开头的话就不需要再加1,如果是以 ...

  7. 数据结构- 串的模式匹配算法:BF和 KMP算法

      数据结构- 串的模式匹配算法:BF和 KMP算法  Brute-Force算法的思想 1.BF(Brute-Force)算法 Brute-Force算法的基本思想是: 1) 从目标串s 的第一个字 ...

  8. 数据结构与算法--KMP算法查找子字符串

    数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章.这篇文章.还有这篇他们写得非常棒.结合他们的解释和自己的理解,完成了本文. 上一节介绍了暴力法查找子字符串,同时也发 ...

  9. 【数据结构】KMP算法

    我还是不太懂... 转2篇大神的解释    1>https://www.cnblogs.com/yjiyjige/p/3263858.html     2>https://blog.csd ...

随机推荐

  1. c#中实现串口通信的几种方法

    c#中实现串口通信的几种方法 通常,在C#中实现串口通信,我们有四种方法: 第一:通过MSCOMM控件这是最简单的,最方便的方法.可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册 ...

  2. fastJson序列化

    在pojo实体中有map<String,Object>的属性,有个key是user它存储在数据库中是用户的id数组,而在aop里会对这个属性做用户详细信息查询并重新put给user.在做J ...

  3. Linux lvm在线扩容

    1.查看磁盘空间 [root@bgd-mysql3 ~]# fdisk -l Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sector ...

  4. malloc() vs new

    Following are the differences between malloc() and operator new. (1)new calls constructors, while ma ...

  5. JavaScript实现数组去重方法

    一.利用ES6 Set去重(ES6中最常用) function unique (arr) { return Array.from(new Set(arr)) } var arr = [1,1,'tru ...

  6. 理解css中的 content:" " 是什么意思

    css中的属性是插入生成的内容,它一般与伪元素:befor和 :after 配合使用. content:"." 就表示在需要的地方插入"." 注意:如果已经规定 ...

  7. idea集成开发工具快捷键大全

    1  执行(run)                                                 alt+r 2  提示补全 (Class Name Completion)    ...

  8. 深度解析Spring Cloud Ribbon的实现源码及原理

    Ribbon的核心作用就是进行请求的负载均衡,它的基本原理如下图所示.就是客户端集成Ribbon这个组件,Ribbon中会针对已经配置的服务提供者地址列表进行负载均衡的计算,得到一个目标地址之后,再发 ...

  9. Tableau如何使用 度量值和度量名称

    一.把子类别拖拽至列 二.度量值拖拽至行 三.度量名称拖拽至筛选器,右键-编辑筛选器-选择销售额和利润 四.度量名称拖拽是标记选择颜色-其它细节调整-最终结果如下所示

  10. box-shadow(盒子阴影)

    box-shadow 属性可以设置一个或多个下拉阴影的框 可以在同一个元素上设置多个阴影效果,并用逗号将他们分隔开.该属性可设置的值包括阴影的X轴偏移量.Y轴偏移量.模糊半径.扩散半径和颜色. 语法: ...