KMP 算法中的 next 数组
KMP 算法中对 next 数组的理解
next 数组的意义
此处 next[j] = k;则有 k 前面的浅蓝色区域和 j 前面的浅蓝色区域相同;
next[j] 表示当位置 j 的字符串与主串不匹配时,下一个需要和主串比较的字串位置在 next[j] 处;有下图:
若当前位置 j 与主串某一个字符不匹配,则下一次比较的是 K 与主串的当前位置,这个 K 也就是next[j];由于两个浅蓝色区域相同,因此 K 前面的区域肯定与主串相同,不需比较;如下图:
由上图可知,K 前面的区域不需比较;
next 数组的推导
从 next 数组所表达的意义可知,我们要求 next[j],首先要找到一个 K,这个 K 前面的浅蓝色区域和 j 前面的浅蓝色区域相同;如下图:
根据规定 next[1] = 0;接下来求其他的 next[j];
对于 next[2] 可得其必然为 next[2] = 1;
如下图:当第二个元素不匹配时,j 将退回到 1 处进行比较,因此 next[2] 一定为 1;
接下来是一般情况的推导,此处使用递推法进行推导,即已知 next[j] 求 next[j + 1];
若 next[j] = k 则有下图:
由于 next[j = k,可知浅蓝色部分相同;接下来分两种情况讨论;
ch[K] == ch[j],这种情况时,可以得到下图;
由图可知:对于 j + 1,能够找到一个 K + 1 使得有浅蓝色区域相同,那么当 j + 1 不匹配时,下一次将比较 K + 1 和主串;因此 next[j + 1] = K + 1 = next[j] + 1;
ch[K] != ch[j],这种情况就变的复杂,这也是整个 KMP 算法中最难理解的部分;
从本节的开头可以知道,求 next[j + 1] 最关键的一点在于求 j + 1 之前有多长的后缀和前缀匹配,即找出多大的浅蓝色区域匹配;我们现在面对的图如下:
我们的目的是找到一个 K1 使得出现下列情况:找到 K1 使得浅蓝色部分相同;
要想浅蓝色部分相同,分为两个部分,使得 1 和 2 相同,使得 K1 和 j 相同;
想要让 1 和 2 相同是难以比较的,但是可以转化为另一个问题,如下图:
想要找出 1 和 3 相同的区域,等价与找到 1 和 2 相同的区域;为什么呢?因为 next[j] = K,因此 j 前面与 K 前面相同如下图:
这个等价关系非常重要,是这部分推导的关键;将其单独抽离出来如下图:
那么如何得到 K1 使得 1 和 2 相同呢?回到文首 next[j] 所表示的意义,next[j] = k;则有 k 前面的浅蓝色区域和 j 前面的浅蓝色区域相同 而 next[K] 是在 j 前面的是已知的,因此可得 K1 = next[K],此时得到的 K1 即可满足 1 和 3 相同;
到此就解决了 1 和 3 相等的问题,直接比较 K1 和 j 若两者相同,则可得到下图;
那么 next[j + 1] = K1 + 1 = next[K] + 1 = next[next[j]] + 1;
那么若 ch[j] != ch[K1] 呢?那么就又演化为如下问题:
这个图和本小节开始的图相同,那么按照此方法解决即可;
可得结果:next[j + 1] = next[K1] + 1 = next[next[K]] + 1 = next[next[next[j]]] + 1
若下一次 K2 依然和 j 不相等,那么又接着递归即可;一直到 Kn = 0;
一个例子
接下来使用上面的结论来计算一个字符串的 next 数组;
有数组 ababaaababaa 转化为如下表:
S | a | b | a | b | a | a | a | b | a | b | a | a |
---|---|---|---|---|---|---|---|---|---|---|---|---|
编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
next | 0 | 1 | 1 | 2 | 3 | 4 | 2 | 2 | 3 | 4 | 5 | 6 |
按如下顺序填表的 next 栏:
- 对于 next[1] 规定为 0,根据前面的分析:next[2] = 1;
- 对于 next[3],则观察 2 和 next[2] = 1,即 b 和 a,不相等;而next[next[2]] = 0,因此 next[3] = 1;
- 对于 next[4],观察 3 和 next[3] = 1,即 a 和 a,相等,故 next[4] = next[3] + 1 = 2;
- 对于 next[5],观察 4 和 next[4] = 2,即 b 和 b,相等,故 next[5] = next[4] + 1 = 3;
- 对于 next[6],观察 5 和 next[5] = 3,即 a 和 a,相等,故 next[6] = next[5] + 1 = 4;
- 对于 next[7],观察 6 和 next[6] = 4,即 a 和 b,不相等,next[next[6]] = 2,与 6 比较即 b 和 a,不相等,继续递归 next[next[next[6]]] = next[next[4]] = next[2] = 1;比较 1 和 6 即 a 和 a,相等,因此 next[6] = next[next[next[6]] + 1 = 2;
- 对于 next[8],观察 7 和 next[7] = 2,即 a 和 b,不相等,next[next[7]] = 1,1 和 7相等,因此 next[8] = next[next[7]] + 1 = next[2] + 1 = 2;
- 对于 next[9],观察 8 和 next[8] = 2,即 b 和 b,相等,故 next[9] = next[8] + 1 = 3;
- 对于 next[10],观察 9 和 next[9] = 1,即 a 和 a,相等,故 next[10] = next[9] + 1 = 4;
- 对于 next[11],观察 10 和 next[10] = 2,即 b 和 b,相等,故 next[11] = next[10] + 1 = 5;
- 对于 next[12],观察 11 和 next[11] = 3,即 a 和 a,相等,故 next[12] = next[11] + 1 = 6;
通过以上分析得到的获取 next 数组的代码如下:
void get_next(String T, int next[]) {
int k = 0, j = 1;
next[1] = 0;
while (j < T.length)
{
if(k == 0 || T.ch[k] == T.ch[j]) {
k++;
j++;
next[j] = k;
} else {
k = next[k];
}
}
}
那么接下来的 KMP 算法代码就比较容易了:
int KMP(String S, String T, int next[]) {
int i = 1, j = 1;
while (i <= S.length && j <= T.length)
{
if(j == 0 || S.ch[i] == T.ch[j]) {
i++;
j++;
} else {
j = next[j];
}
}
if(j > T.length) {
return i - T.length;
} else {
return 0;
}
}
测试代码如下:
#include<iostream>
using namespace std;
const int MAX = 255;
typedef struct {
char ch[MAX];
int length;
} String;
void InitiString(String &s, char chars[]) {
int len = 0;
while(chars[len] != '\0') {
s.ch[len + 1] = chars[len];
len++;
}
s.length = len;
}
void get_next(String T, int next[]) {
int k = 0, j = 1;
next[1] = 0;
while (j < T.length)
{
if(k == 0 || T.ch[k] == T.ch[j]) {
k++;
j++;
next[j] = k;
} else {
k = next[k];
}
}
}
int KMP(String S, String T, int next[]) {
int i = 1, j = 1;
while (i <= S.length && j <= T.length)
{
if(j == 0 || S.ch[i] == T.ch[j]) {
i++;
j++;
} else {
j = next[j];
}
}
if(j > T.length) {
return i - T.length;
} else {
return 0;
}
}
int main() {
char char1[20] = "aabaabaabaac";
char char2[20] = "aabaac";
String S, T;
InitiString(S, char1);
InitiString(T, char2);
int next[MAX];
get_next(T, next);
int index = KMP(S, T, next);
printf("%d", index);
return 0;
}
输出结果:
7
KMP 算法中的 next 数组的更多相关文章
- KMP算法中求next数组的实质
在串匹配模式中,KMP算法较蛮力法是高效的算法,我觉得其中最重要的一点就是求next数组: 看了很多资料才弄明白求next数组是怎么求的,我发现我的忘性真的比记性大很多,每次看到KMP算法求next数 ...
- kmp算法中的next数组实例解释
假设求串′ababaaababaa′的next数组 模式串 a b a b a a a b a b a a 下标 1 2 3 4 5 6 7 8 9 10 11 12 1.前两位:next数组前两位一 ...
- KMP算法中的next数组求解示意图
- 数据结构KMP算法中手算next数组
总结一下今天的收获(以王道数据结构书上的为例子,虽然我没看它上面的...):其中竖着的一列值是模式串前缀和后缀最长公共前缀. 最后求得的结果符合书上的结果,如果是以-1开头的话就不需要再加1,如果是以 ...
- KMP算法中我对获取next数组的理解
之前在学KMP算法时一直理解不了获取next数组的函数是如何实现的,现在大概知道怎么一回事了,记录一下我对获取next数组的理解. KMP算法实现的原理就不再赘述了,先上KMP代码: 1 void g ...
- 问题 1690: 算法4-7:KMP算法中的模式串移动数组
题目链接:https://www.dotcpp.com/oj/problem1690.html 题目描述 字符串的子串定位称为模式匹配,模式匹配可以有多种方法.简单的算法可以使用两重嵌套循环,时间复杂 ...
- KMP算法中next数组的理解与算法的实现(java语言)
KMP 算法我们有写好的函数帮我们计算 Next 数组的值和 Nextval 数组的值,但是如果是考试,那就只能自己来手算这两个数组了,这里分享一下我的计算方法吧. 计算前缀 Next[i] 的值: ...
- 关于KMP算法中,获取next数组算法的理解
参考:KMP入门级别算法详解--终于解决了(next数组详解) https://blog.csdn.net/lee18254290736/article/details/77278769 在这里讨论的 ...
- KMP算法中next数组的构建
记得初学$kmp$的时候 老师让大家把它直接背下来 然而不理解的话 不仅调试起来比较慢 很多题目也难往$kmp$上想 ----------------------------------------- ...
随机推荐
- SpringBoot外部配置属性注入
一.命令行参数配置 Spring Boot可以是基于jar包运行的,打成jar包的程序可以直接通过下面命令运行: java -jar xx.jar 那么就可以通过命令行改变相关配置参数.例如默认tom ...
- DBLink的使用(从A库使用SQL查询B库的数据)
DBLink的使用 情景:今天我需要从A数据库查询B数据库的数据,进行一些数据比对和联合查询的操作. 所以用到的DBLink,在此记录一下使用流程,希望能够帮助下一个小白,一步到位的解决问题. 一句话 ...
- 本塔科技借力VR Engine,提供更优质的3D VR内容
本文分享于华为开发者论坛<本塔科技借力HMS Core VR Engine,提供更优质的3D VR视频内容> 本塔科技致力于为影视行业提供优质的3D VR视频内容,追求稳定.观看感舒适.良 ...
- 『无为则无心』Python面向对象 — 57、类属性和实例属性
目录 1.类属性 (1)类属性的访问 (2)修改类属性 2.类属性和实例属性区别 1.类属性 (1)类属性的访问 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有. 类属性可以使用 ...
- OpenStack学习系列之十二:安装ceph并对接OpenStack
Ceph 是一种为优秀的性能.可靠性和可扩展性而设计的统一的.分布式文件系统.Ceph 的统一体现在可以提供文件系统.块存储和对象存储,分布式体现在可以动态扩展.在国内一些公司的云环境中,通常 ...
- Win10搭建web服务实现文件共享
配置Internet Information Services服务,实现文件共享. 1.打开控制面板,启用IIS服务. 启用IIS服务,系统会自动搜索并安装IIS服务. 2.管理web服务 此时在浏览 ...
- 【C# 集合】HashTable .net core 中的Hashtable的实现原理
上一篇我介绍了Hash函数 这篇我来说一下Hash函数在 HashTable中的应用. HashTable的特性: 1.装载因子:.net core 0.72 ,java 0.75 2.冲突解决方案: ...
- C#操作WMI指南
WMI应用(一个系统自带的测试WMI语句的工具) 1. 开始-运行-输入:wbemtest 回车2. 单击"连接", 输入:root\cimv2 回车; 或者ROOT\Securi ...
- QT:Qt Creator中创建的各种项目说明
Qt Widgets Application:支持桌面平台GUI的应用程序.GUI的设计完全基于C++,采用Qt提供的一套C++类库: Qt Console Application:控制台应用程序,无 ...
- 爬虫之爬取豆瓣top250电影排行榜及爬取斗图啦表情包解读及爬虫知识点补充
今日内容概要 如何将爬取的数据直接导入Excel表格 #如何通过Python代码操作Excel表格 #前戏 import requests import time from openpyxl impo ...