<数据结构>KMP算法
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]成立
实现
步骤
- 初始化next数组,next[0] = j = -1
- 令i由1-(len-1)重复 3. 4.
- 不断令 j = next[j], 知道 j !=-1 或者 s[i] == s[j+1],
- 如果 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的子串
实现
步骤
- 初始化j=1
- 让i遍历text数组,对每个i,执行3.4.来试图匹配text[i]和patten[j+1]
- 不断令 j = next[j],直到 j == -1或 text[i] == patten[j+1]
- 如果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算法的更多相关文章
- 数据结构--KMP算法总结
数据结构—KMP KMP算法用于解决两个字符串匹配的问题,但更多的时候用到的是next数组的含义,用到next数组的时候,大多是题目跟前后缀有关的 . 首先介绍KMP算法:(假定next数组已经学会, ...
- 实验数据结构——KMP算法Test.ming
翻译计划 小明初学者C++,它确定了四个算术.关系运算符.逻辑运算.颂值操作.输入输出.使用简单的选择和循环结构.但他的英语不是很好,记住太多的保留字,他利用汉语拼音的保留字,小屋C++,发明 ...
- 数据结构——KMP算法
算法介绍 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法).KMP算法的核心是利用 ...
- 数据结构-kmp算法
定义 改进字符串的匹配算法 关键:通过实现一个包含了模式串的局部匹配信息的next()函数,利用匹配失败的信息,减少匹配次数. 1.BF算法 暴力匹配 给定 文本串S "BBC ABCDAB ...
- 大话数据结构——KMP算法(还存在问题)
http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html /*#include& ...
- 数据结构KMP算法中手算next数组
总结一下今天的收获(以王道数据结构书上的为例子,虽然我没看它上面的...):其中竖着的一列值是模式串前缀和后缀最长公共前缀. 最后求得的结果符合书上的结果,如果是以-1开头的话就不需要再加1,如果是以 ...
- 数据结构- 串的模式匹配算法:BF和 KMP算法
数据结构- 串的模式匹配算法:BF和 KMP算法 Brute-Force算法的思想 1.BF(Brute-Force)算法 Brute-Force算法的基本思想是: 1) 从目标串s 的第一个字 ...
- 数据结构与算法--KMP算法查找子字符串
数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章.这篇文章.还有这篇他们写得非常棒.结合他们的解释和自己的理解,完成了本文. 上一节介绍了暴力法查找子字符串,同时也发 ...
- 【数据结构】KMP算法
我还是不太懂... 转2篇大神的解释 1>https://www.cnblogs.com/yjiyjige/p/3263858.html 2>https://blog.csd ...
随机推荐
- c#中实现串口通信的几种方法
c#中实现串口通信的几种方法 通常,在C#中实现串口通信,我们有四种方法: 第一:通过MSCOMM控件这是最简单的,最方便的方法.可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册 ...
- fastJson序列化
在pojo实体中有map<String,Object>的属性,有个key是user它存储在数据库中是用户的id数组,而在aop里会对这个属性做用户详细信息查询并重新put给user.在做J ...
- Linux lvm在线扩容
1.查看磁盘空间 [root@bgd-mysql3 ~]# fdisk -l Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sector ...
- malloc() vs new
Following are the differences between malloc() and operator new. (1)new calls constructors, while ma ...
- JavaScript实现数组去重方法
一.利用ES6 Set去重(ES6中最常用) function unique (arr) { return Array.from(new Set(arr)) } var arr = [1,1,'tru ...
- 理解css中的 content:" " 是什么意思
css中的属性是插入生成的内容,它一般与伪元素:befor和 :after 配合使用. content:"." 就表示在需要的地方插入"." 注意:如果已经规定 ...
- idea集成开发工具快捷键大全
1 执行(run) alt+r 2 提示补全 (Class Name Completion) ...
- 深度解析Spring Cloud Ribbon的实现源码及原理
Ribbon的核心作用就是进行请求的负载均衡,它的基本原理如下图所示.就是客户端集成Ribbon这个组件,Ribbon中会针对已经配置的服务提供者地址列表进行负载均衡的计算,得到一个目标地址之后,再发 ...
- Tableau如何使用 度量值和度量名称
一.把子类别拖拽至列 二.度量值拖拽至行 三.度量名称拖拽至筛选器,右键-编辑筛选器-选择销售额和利润 四.度量名称拖拽是标记选择颜色-其它细节调整-最终结果如下所示
- box-shadow(盒子阴影)
box-shadow 属性可以设置一个或多个下拉阴影的框 可以在同一个元素上设置多个阴影效果,并用逗号将他们分隔开.该属性可设置的值包括阴影的X轴偏移量.Y轴偏移量.模糊半径.扩散半径和颜色. 语法: ...