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. spring boot 之监听器ApplicationListener

    监听器ApplicationListener 就是spring的监听器,能够用来监听事件,典型的观察者模式.ApplicationListener和ContextRefreshedEvent一般都是成 ...

  2. 【MPI环境配置】 vs2019配置MPI环境

    MPI 即 Message-Passing Interface,提供了一系列并行编程的接口,为了在本机能够学习和使用并行编程,需要提前安装MPI; 配置环境: Microsoft Visual Stu ...

  3. oracle to_char处理日期

    select to_char(sysdate,'d') from dual;--本周第几天 select to_char(sysdate,'dd') from dual;--本月第几天 select ...

  4. MySQL(2):数据管理

    一. 外键概念: 如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键.由此可见,外键表示了两个关系之间的相关联系.以另一个关系的外键作主关键字的表被称为主表,具有此外键的表 ...

  5. 【简】题解 AWSL090429 【原子】

    预处理出每个原子最近的不能合并的位置 枚举当前位置和前面断开的位置合并 发现还是不能过 考虑用选段树优化 但是因为每次转移的最优点是在前面可以合并的范围内 dp值加上当前的到该点的最大值 因为每个位置 ...

  6. Decorator 模式转载

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://tianli.blog.51cto.com/190322/35287 摘要:本文深 ...

  7. house of force----gyctf_2020_force!!

    做这道题前线学习一下house of force的用法 Linux下堆溢出利用2-House of force基本原理_haibiandaxia的博客-CSDN博客 老样子例行检查(这里我就不放了) ...

  8. CF1145F Neat Words 题解

    Content 本题为 CF 愚人节比赛题目. 给定一个长度为 \(n\) 的,仅包含大写字母的字符串,问这个字符串里面的字母是否全部都是由仅有直线的字母组成,或者全部都是由有曲线的字母构成. 数据范 ...

  9. ViewModel的创建

    ViewModel的创建 ViewModel本身只是ViewModel这个类的子类: class MainViewModel: ViewModel() { } 在屏幕旋转UI重建的时候, 它是如何拥有 ...

  10. Linux(debian7)操作基础(四)之CPU频率调整 Linux系统CPU频率调整工具使用

    在Linux中,内核的开发者定义了一套框架模型来完成CPU频率动态调整这一目的,它就是CPU Freq系统.如下为CPU的几种模式(governor参数): ondemand:系统默认的超频模式,按需 ...