<数据结构>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 ...
随机推荐
- 大数据学习day33----spark13-----1.两种方式管理偏移量并将偏移量写入redis 2. MySQL事务的测试 3.利用MySQL事务实现数据统计的ExactlyOnce(sql语句中出现相同key时如何进行累加(此处时出现相同的单词))4 将数据写入kafka
1.两种方式管理偏移量并将偏移量写入redis (1)第一种:rdd的形式 一般是使用这种直连的方式,但其缺点是没法调用一些更加高级的api,如窗口操作.如果想更加精确的控制偏移量,就使用这种方式 代 ...
- 零基础学习java------day14-----泛型,foreach,可变参数,数组和集合间的转换,Set,Map,
1.泛型(jdk1.5以后出现) https://www.cnblogs.com/lwbqqyumidi/p/3837629.html#!comments (1)为什么要用泛型? 限制集合,让它只能存 ...
- Angular @ViewChild,Angular 中的 dom 操作
Angular 中的 dom 操作(原生 js) ngAfterViewInit(){ var boxDom:any=document.getElementById('box'); boxDom.st ...
- Linux基础命令---echo打印内容到标准输出
echo echo指令可以输出内容到标准输出,以空白分割字符串,并且后面增加换行. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.Fedora. 1.语法 ec ...
- Linux lvm在线扩容
1.查看磁盘空间 [root@bgd-mysql3 ~]# fdisk -l Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sector ...
- API测试最佳实践 - 身份验证
适用等级:高级 1. 概况 身份验证通常被定义为是对某个资源的身份的确认的活动,这里面资源的身份指代的是API的消费者(或者说是调用者).一旦一个用户的身份验证通过了,他将被授权访问那些期待访问的资源 ...
- KVM配置
安装依赖包(因最小化安装) [root@slave-master ~]# yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pc ...
- window安装ab压力测试
ab是Apache HTTP server benchmarking tool的缩写,可以用以测试HTTP请求的服务器性能,也是业界比较流行和简单易用的一种压力测试工具包 ## 下载 下载地址:(ht ...
- highchars操作集合
一.tooltip 与鼠标指针的距离想调整tooltip和鼠标指针的距离,官方api 和中文api中都没写,只有轴 label.distance . 但我觉得应该有这个,看源码果然有 tooltip ...
- 那些年采的python的坑
1:使用virtualenvwrapper 新建虚拟环境时出现的错误 OSError: Command D:\file\python\virtu...r\Scripts\python.exe - se ...