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. Kafka(一)【概述、入门、架构原理】

    目录 一.Kafka概述 1.1 定义 二.Kafka快速入门 2.1 安装部署 2.2 配置文件解析 2.3Kafka群起脚本 2.4 topic(增删改查) 2.5 生产和消费者命令行操作 三.K ...

  2. 【2021赣网杯web(一)】gwb-web-easypop

    源码分析 <?php error_reporting(0); highlight_file(__FILE__); $pwd=getcwd(); class func { public $mod1 ...

  3. vue2.x入门学习

    vue安装 # 最新稳定版本 $ npm install vue # 最新稳定 CSP 兼容版本 $ npm install vue@csp 引包 cd /d/vue/demo cnpm instal ...

  4. vue2 页面路由

    vue官方文档 src/views/Login.vue <template> <div> <h2>登录页</h2> </div> </ ...

  5. Can a C++ class have an object of self type?

    A class declaration can contain static object of self type,it can also have pointer to self type,but ...

  6. linux系统下安装dubbo-admin

    1.在安装dubbo-admin之前确保你得linux服务器上已经成功安装了jdk,tomcat, 若还没安装jdk以及tomcat则参考我的上一篇文章"linux环境下安装jdk,tomc ...

  7. Java学习1:图解Java内存分析详解(实例)

    首先需要明白以下几点: 栈空间(stack),连续的存储空间,遵循后进先出的原则,用于存放局部变量. 堆空间(heap),不连续的空间,用于存放new出的对象,或者说是类的实例. 方法区(method ...

  8. Windows下搭建FFmpeg开发调试环境

    背景 如果你是一个FFmpeg的使用者,那么绝大部分情况下只需要在你的程序中引用FFmpeg的libav*相关的头文件,然后在编译阶段链接相关的库即可. 但是如果你想调试FFmpeg内部相关的逻辑,或 ...

  9. js--对象内部属性与 Object.defineProperty()

    前言 JavaScript 中允许使用一些内部特性来描述属性的特征,本文来总结一下对象内部属性与 Object.defineProperty() 的相关知识. 正文 1.属性类型 js中使用某些内部属 ...

  10. 小迪安全 Web安全 基础入门 - 第十天 - 信息打点-APP&小程序篇&抓包封包&XP框架&反编译&资产提取

    一.本节知识点思维导图 二.APP-外在资产收集 1.将APP安装在模拟器中,修改模拟器代理设置,使用Fiddler.Burpsuite.Charles等抓包工具抓取APP访问的http协议数据包,抓 ...