KMP算法以及优化(代码分析以及求解next数组和nextval数组)
KMP算法以及优化(代码分析以及求解next数组和nextval数组)
来了,数据结构及算法的内容来了,这才是我们的专攻,前面写的都是开胃小菜,本篇文章,侧重考研408方向,所以保证了你只要看懂了,题一定会做,难道这样思想还会不会么?如果只想看next数组以及nextval数组的求解可以直接跳到相应部分,思想总结的很干~~
网上的next数组版本解惑
先总结一下,一般KMP算法的next数组结果有两个版本,我们需要知道为什么会存在这种问题,其实就是前缀和后缀没有匹配的时候next数组为0还是为1,两个版本当然都是对的了,如果next数组为0是的版本,那么对于前缀和后缀的最大匹配长度只需要值+1就跟next数组是1的版本一样了,其实是因为他们的源代码不一样,或者对于模式串的第一个下标理解为0或者1,总之这个问题不用纠结,懂原理就行~~
那么此处,我们假定前缀和后缀的最大匹配长度为0时,next数组值为1的版本,考研一般都是用这个版本(如果为0版本,所有的内容-1即可,如你算出next[5]=6,那么-1版本的next[5]就为5,反之亦然)~~
其实上面的话总结就是一句话
next[1]=0,j(模式串)数组的第一位下标为1,同时,前缀和后缀的最大匹配长度+1即为next数组的值,j所代表的的是序号的意思
408反人类,一般数组第一位下标为1,关于书本上前面链表的学习大家就应该有目共睹了,书本上好多数组的第一位下标为了方便我们理解下标为1,想法这样我们更不好理解了,很反人类,所以这里给出next[1]=0,前缀和后缀的最大匹配长度+1的版本讲解
前言以及问题引出
我们先要知道,KMP算法是用于字符串匹配的~~
例如:一个主串"abababcdef"我们想要知道在其中是否包括一个模式串"ababc"
初代的解决方法是,朴素模式匹配算法,也就是我们主串和模式串对比,不同主串就往前移一位,从下一位开始再和模式串对比,每次只移动一位,这样会很慢,所以就有三位大神一起搞了个算法,也就是我们现在所称的KMP算法~~
代码以及理解
源码这里给出~~
int Index_KMP(SString S,SString T,intt next[]){
int i = 1,j = 1;//数组第一位下标为1
while (i <= S.length && j <= T.length){
if (j == 0 || S.ch[i] == T.ch[j]){//数组第一位下标为1,0的意思为数组第一位的前面,此时++1,则指向数组的第一位元素
++i;
++j; //继续比较后继字符
}
else
j = next[j]; //模式串向右移动到第几个下标,序号(第一位从1开始)
}
if (j > T.length)
return i - T.length; //匹配成功
else
return 0;
}
接下来就可以跟我来理解这个代码~~
还不会做动图,这里就手画了~~

以上是一般情况,那么如何理解j=next[1]=0的时候呢?

是的,这就是代码的思路,那么这时我们就知道,核心就是要求next数组各个的值,对吧,一般也就是考我们next数组的值为多少~~
next数组的求解
这里先需要给出概念,串的前缀以及串的后缀~~
串的前缀:包含第一个字符,且不包含最后一个字符的子串
串的后缀:包含最后一个字符,且不包含第一个字符的子串
当第j个字符匹配失败,由前1~j-1个字符组成的串记为S,则:next[j]=S的最长相等前后缀长度+1
与此同时,next[1]=0
如,模式串"ababaa"
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 |
当第六个字符串匹配失败,那么我们需要在前5个字符组成的串S"ababa"中找最长相等的前后缀长度为多少再+1~~
如串S的前缀可以为:"a","ab","aba","abab",前缀只不包括最后一位都可
串S的后缀可以为:"a","ba","aba","baba",后缀只不包括第一位都可
所以这里最大匹配串就是"aba"长度为3,那么我们+1,取4
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 4 |
再比如,当第二个字符串匹配失败,由前1个字符组成的串S"a"中,我们知道前缀应当没有,后缀应当没有,所以最大匹配串应该为0,那么+1就是取1~~
其实这里我们就能知道一个规律了,next[1]一定为0(源码所造成),next[2]一定为1(必定没有最大匹配串造成)~~
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 4 |
再再比如,第三个字符串匹配失败,由前两个字符组成的串S"ab"中找最长相等的前后缀长度,之后再+1~~
前缀:"a"
后缀:"b"
所以所以这里最大匹配串也是没有的长度为0,那么我们+1,取1
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 4 |
接下来你可以自己练练4和5的情况~~
这里直接给出答案~~
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
是不是很简单呢?
至此,next数组的求法以及kmp代码的理解就ok了~~
那么接下来,在了解以上之后,我们想一想KMP算法存在的问题~~
KMP算法存在的问题
如下
主串:"abcababaa"
模式串:"ababaa"
例如这个问题
我们很容易能求出next数组
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
此时我们是第三个字符串匹配失败,所以我们的next[3]=1,也就是下次就是第一个字符"a"和主串中第三个字符"c"对比,可是我们刚开始的时候就已经知道模式串的第三个字符"a"和"c"不匹配,那么这里不就多了一步无意义的匹配了么?所以我们就会有kmp算法的一个优化了~~
KMP算法的优化
我们知道,模式串第三个字符"a"不和主串第三个字符"c"不匹配,next数组需要我们的next[3]=1,也就是下次就是第一个字符"a"和主串中第三个字符"c"对比,之后就是模式串第一个字符"a"不和"c"匹配,就是需要变为next[1]=0,那么我们要省去步骤,不就可以直接让next[3]=0么?
| 序号J | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a |
| next[j] | 0 | 1 | 1 | 2 | 3 |
| nextval[j] | 0 | 0 |
那么怎么省去多余的步骤呢?
这就是nextval数组的求法~~
nextval的求法以及代码理解
先贴出代码
for (int j = 2;j <= T.length;j++){
if (T.ch[next[j]] == T.ch[j])
nextval[j] = nextval[next[j]];
else
nextval[j] = next[j];
}
如
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 |
首先,第一次for循环,j=2,当前序号b的next[2]为1,即第一个序号所指向的字符a,a!=当前序号b,所以nextval[2]保持不变等于next[2]=1
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 | 1 |
第二次for循环,j=3,当前序号a的next[3]为1,即第一个序号所指向的字符a,a=当前序号a,所以nextval[3]等于nextval[1]=0
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 | 1 | 0 |
第三次for循环,j=4,当前序号b的next[4]为2,即第二个序号所指向的字符b,b=当前序号b,所以nextval[4]等于nextval[2]=1
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 | 1 | 0 | 1 |
就是这样,你可以练练5和6,这里直接给出~~
| 序号J | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | b | a | a |
| next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 | 1 | 0 | 1 | 0 | 4 |
至此nextval数组的求法你也应该会了,那么考研要是考了,那么是不是就等于送分给你呢?
小练习
那么你试着来求一下这个模式串的next和nextval数组吧~~
模式串:"aaaab"
| 序号j | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 模式串 | a | a | a | a | b |
| next[j] | |||||
| nextval[j] |
小练习的答案
| 序号j | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 模式串 | a | a | a | a | b |
| next[j] | 0 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 | 0 | 0 | 0 | 4 |
KMP算法以及优化(代码分析以及求解next数组和nextval数组)的更多相关文章
- KMP算法的优化与详解
文章开头,我首先抄录一些阮一峰先生关于KMP算法的一些讲解. 下面,我用自己的语言,试图写一篇比较好懂的 KMP 算法解释. 1. 首先,字符串"BBC ABCDAB ABCDABCDABD ...
- KMP算法实践与简单分析
一.理解next数组 1.约定next[0]=-1,同时可以假想在sub串的最前面有一个通配符"*",能够任意匹配.对应实际的代码t<0时的处理情况. 2.next[j]可以 ...
- 算法 | 串匹配算法之KMP算法及其优化
主串 s:A B D A B C A B C 子串 t: A B C A B 问题:在主串 s 中是否存在一段 t 的子串呢? 形如上述问题,就是串匹配类问题.[串匹配--百度百科] 串匹配问题是一 ...
- 字符串匹配KMP算法中Next[]数组和Nextval[]数组求法
数据结构课本上给了这么一段算法求nextval9[]数组 int get_nextval(SString T,int &nextval[ ]) { //求模式串T的next函数修正值并存入数组 ...
- 【原创】通俗易懂的讲解KMP算法(字符串匹配算法)及代码实现
一.本文简介 本文的目的是简单明了的讲解KMP算法的思想及实现过程. 网上的文章的确有些杂乱,有的过浅,有的太深,希望本文对初学者是非常友好的. 其实KMP算法有一些改良版,这些是在理解KMP核心思想 ...
- KMP算法的正确性证明及一个小优化
直接把作业帖上来是不是有点不太公道呀... 无所谓啦反正各位看着开心就行 KMP算法 对于模式串$P$,建立其前缀函数$ N$ ,其中$N [q] $ 表示在$P$中,以$q$位置为结束的可以匹配到前 ...
- KMP算法的一次理解
1. 引言 在一个大的字符串中对一个小的子串进行定位称为字符串的模式匹配,这应该算是字符串中最重要的一个操作之一了.KMP本身不复杂,但网上绝大部分的文章把它讲混乱了.下面,咱们从暴力匹配算法讲起,随 ...
- 字符串匹配的 KMP算法
一般字符串匹配过程 KMP算法是字符串匹配算法的一种改进版,一般的字符串匹配算法是:从主串(目标字符串)和模式串(待匹配字符串)的第一个字符开始比较,如果相等则继续匹配下一个字符, 如果不相等则从主串 ...
- 算法笔记之KMP算法
本文是<算法笔记>KMP算法章节的阅读笔记,文中主要内容来源于<算法笔记>.本文主要介绍了next数组.KMP算法及其应用以及对KMP算法的优化. KMP算法主要用于解决字符串 ...
随机推荐
- 在ASP.NET Core中用HttpClient(四)——提高性能和优化内存
到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容.但是我们可以通过使用流提高性能和优化内存.因此,在本文中,我们将学习如何在请求和响应中使用HttpClient流. 什么是流 流是以文件. ...
- AutoPy开发文档
AutoPy 简介 AutoPy是为python开发者提供的一个安卓插件,由路飞大佬开发维护,主要功能为了实现使用python在安卓端完成一些操作,例如点击,滑动,返回 准备 安装AutoPy.apk ...
- docker使用常见问题解决方案:错误号码2058,docker WARNING :IPv4,容器间的通讯
1.错误号码2058 1,错误解决: 解决方法:docker下mysql容器 登录 mysql -u root -p 登录你的 mysql 数据库,然后 执行这条SQL: ALTER USER 'ro ...
- [leetcode] 单调栈
本文总结单调栈算法. 原问题 学习一个算法,我们需要清楚的是:这个算法最原始的问题背景是什么样的? 下一个更小元素 给定一个数组 nums,返回每个元素的下一个更小的元素的下标 res,即 res[i ...
- 结对编程-stage_2
教学班 罗杰.任建班周五3.4节 gitlab项目地址 Here it is. 成员 周远航(3004) 李辰洋(3477) 结对编程体验 经过了上一阶段的磨合,第二阶段我们的配合更加流畅,也熟悉了对 ...
- VirtualBox虚拟机读取U盘
1 概述 使用VirtualBox虚拟机(系统Win10)读取宿主机(系统Manjaro)中的U盘. 2 安装扩展 戳这里下载对应版本的一个叫Oracle_VM_VirtualBox_Extensio ...
- VUE+Element 前端应用开发框架功能介绍
前面介绍了很多ABP系列的文章<ABP框架使用>,一步一步的把我们日常开发中涉及到的Web API服务构建.登录日志和操作审计日志.字典管理模块.省份城市的信息维护.权限管理模块中的组织机 ...
- Docker笔记(二) 安装常用软件
常用环境安装 这里收集一些常用环境的安装步骤,可直接复制进行使用,节省安装时间,后续会随时补充 安装MySQL5.7.32 下载mysql5.7.32的镜像 ( 记得下载镜像要先确保镜像仓库中存在这个 ...
- 2021S软件工程——个人阅读作业2
2021S软件工程--个人阅读作业2 项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任建) 这个作业的要求在哪里 个人阅读作业#2 我在这个课程的目标是 了解并熟悉软件开发的具体流程, ...
- 【工具库】Java实体映射工具MapStruct
一.什么是MapStruct? MapStruct是用于代码中JavaBean对象之间的转换,例如DO转换为DTO,DTO转换为VO,或Entity转换为VO等场景,虽然Spring库和 Apache ...