<数据结构>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 ...
随机推荐
- 在 Qualys SSL Labs SSL 测试中获得 A+ 评级的秘技 2021 版
本系列文章将阐述主流应用交付控制器和主流 Web 服务器如何运行 HTTP/2 和 TLSv1.3 协议,以及如何在 SSL Test 中获得 A+ 评级. 请访问原文链接:https://sysin ...
- oracle(创建数据库对象)
1 --创建数据库 2 --1.SYSDBA系统权限 3 startup:--启动数据库. 4 shutdown:--关闭数据库. 5 alter database[mount]|[open]|[ba ...
- Git命令行演练-团队开发
** 团队开发必须有一个共享库,这样成员之间才可以进行协作开发** ### 0. 共享库分类 > 本地共享库(只能在本地面对面操作) - 电脑文件夹/U盘/移动硬盘 & ...
- 【编程思想】【设计模式】【行为模式Behavioral】备忘录模式Memento
Python版 https://github.com/faif/python-patterns/blob/master/behavioral/memento.py #!/usr/bin/env pyt ...
- 【Linux】【Services】【DNS】使用Bind搭建DNS服务
1. 简介 1.1. 实现的功能:DNS解析以及智能转发 1.2. 官方文档: 1.3. 基础概念:http://www.cnblogs.com/demonzk/p/6494968.html 2. 环 ...
- Linux 易错小结
修改文件夹(递归修改)权限 chmod -R 777 /html Linux查看进程的4种方法 第一种: ps aux ps命令用于报告当前系统的进程状态.可以搭配kill指令随时中断.删除不必要的程 ...
- Mysql资料 索引
目录 一.介绍 什么是索引? 为什么要有索引呢? 二.索引的原理 原理 磁盘IO与预读 索引的数据结构 b+树的查找过程 b+树性质 三.索引管理 MySQL的索引分类 各索引应用场景 索引类型 操作 ...
- SQLserver 2014 如何使用Datename()函数获取对应时间
一.在本文中,GetDate()获得的日期由两部分组成,分别是今天的日期和当时的时间: Select GetDate() 二.用DateName()就可以获得相应的年.月.日,然后再把它们连接起来就可 ...
- 大规模服务网格性能优化 | Aeraki xDS 按需加载
作者 钟华,腾讯云专家工程师,Istio project member.contributor,专注于容器和服务网格,在容器化和服务网格生产落地方面具有丰富经验,目前负责 Tencent Cloud ...
- 几个主流TCP/IP协议栈介绍
我们知道TCP IP协议栈内包括了诸多协议.那么对于这当中的协议的功能以及作用,我们来具体了解一下吧.现在让我们做一个盘点,帮助大家总结一下,还望对大家能够有所帮助. 1.BSD TCP IP协议栈 ...