数据结构与算法基础(王卓)(17):KMP算法详解(精讲(最简单、直接、有效的思路方法,答案以及代码原理)
本文具体思路参考: (最后证明,该教材/网课实际上是最有效的)
DS第四章【3】KMP1_哔哩哔哩_bilibili
中间走的一些弯路的教材:
第06周05--第4章串、数组和广义表5-4.3串的操作--串的匹配算法2--KMP算法_哔哩哔哩_bilibili
课本
走弯路过程,详见
数据结构与算法基础(王卓)(16):KMP算法(个人学习历程)
虽然里面学的过程用的很多的是生硬的笨方法,但是里面遇到的问题和踩的坑还是值得一看来更深理解KMP算法的
PART 1:关于next [ j ]
PPT:P30 (根据DS第四章【3】KMP1_哔哩哔哩_bilibili)
需求:
返回子串和主串匹配时候的位置
思想:
把上面的主串((也就是)箭头左边的(前面的)那一部分)当成箭头左边的子串的一个分身
注:比较到不匹配时,箭头(指针)指向不匹配的字符
他们之间公共的部分:
要么是完全对其时候的全部
要么是下面的子串移动到其(主串)公共后缀的位置的时候
其他上下两个串摆放的位置,都不可能产生我们想要的,公共的部分
其实分析这个问题的时候我们就已经不用去看主串的位置和情况了,因为其实我们已经知道:
在匹配不上的前面(箭头左边),子串和主串都是一样的,所以其实只要看子串就行了
所以我们只需要找出前面(箭头左边)的公共前后缀
然后往前(右)移动子串,让他的前缀移动到原来后缀的位置
就可以解决这个问题,往下比较了
核心:
直接把前缀移动到(移动)之前后缀所处的位置,跳过这中间所有的字符
好了,现在我们思路有了,接下来:
具体落实怎么移动:
移动目的:找到主串子串完全匹配的位置
如果发生的是主串和子串比较的字符匹配的情况,我觉得我们根本不用讨论
现在比较的这一位匹配,那就比较下一位
如果一路匹配下去一直都是能匹配的上的话,那就返回结果:
子串放在第一格,可以和主串匹配上
就行了
所以我们需要讨论解决的,是当每一位发生了不匹配的操作时,我们怎么相对应的处理
另外,我们这里不仅要知道下一步该具体怎么移动,还需要思考:
如何用指针实现进一步的比较(所有的探究、思路,都是为了写出程序服务)
现在,我们知道了当每一步(每一种)情况,我们应该怎么进行操作
根据上图,我们就得到了下一步如何操作(把子串的指针移动到哪个位置的操作)的
公式:
字符串从位序 1 开始存储时:

字符串从位序 0 开始存储时:

以上都是怎么进行移动的公式、思路、理论,接下来我们是要写代码的:
先不写KMP算法代码,我们先写求:每一位匹配不上情况的next(把子串的指针移动到哪个位序)
当我们一个一个排(匹配)下去,显然,当我们比较(两个字符)的时候:
- 这个字符前面的所有字符都应该是匹配的,如果不匹配,我们就移动子串的指针位置,重新从头开始比较
- 这个字符前面的所有字符的 next 数组的信息,我们都是可以知道的
所以
现在,我们每往下走一步面临的问题就是:
主串和子串比较的字符匹配【if(1)】,讨论无意义
if (k == -1 || T.ch[k] == T.ch[j]){j++;k++;next[j] = k;}
所以讨论不匹配时【if(0)】,怎么处理:我们面临的情况:
再次强调:
我们要把思路转变一下
不是说我们求的是next【j】
而是我们求的是next【j+1】
我们需要的是从过去推出未来,而不是说
未来是什么我不知道,然后我再去猜过去式这种情况还是那种情况
那种的情况(不等)又只知道一半
只知道最后面一个字符不匹配,在前面的就不知道了
这样研究的话,那还不如具象化的研究
不要让自己走向未知的方向。走进越来越未知的方向
从已知走向未知,让未知走向已知
把上面的这个模式串(子串)分身【位序 j - k 到 k - 1 】看成主串
把下面的这个模式串(子串)分身【位序 0 到 k - 1 】看成子串
于是该问题转化为:
主串与模式串最后一个字符不匹配
然后同样的,比较;比较什么:
子串里有没有公共前后缀
确定后面该移到哪个位置
特别注意:
不要遗漏已知条件:(这个条件和之前主串子串匹配同样类似/相似)
在我们这个不匹配的字符前面,所有的字符全部都匹配
现在我们回到解决问题的
核心:子串(模式串)该移到哪个位置
kmp算法:
同样的,找到这里子串【0到k】前面的公共前后缀
然后把前缀移动到后缀的位置,看看新的前缀后面的字符能不能和第 j 位匹配得上
操作上面的图已经写了:
子串指针移动到【k + 1】位序
elsek = next[k];
如果我们一定想用bf算法:(证明我们真的熟练掌握了这种思想)
如果我们用bf算法,也就是说一格一格往右边移
主串位置不变,子串一格一格往右边移动,一次一次比较
直到匹配成功到 前缀后缀一样 乃至子串和主串一样为止
所以我们要进行的操作就是:
主串指针不变,子串指针不断指向前面一个字符,下一次比较
实际操作就是:k--:
elsek--;
再次强调:
匹配“下一位字符”之前,我们不用担心
他(这个“下一位字符”)和主串前面部分的字符能否匹配得上
这个问题的
即使这个“下一位字符”不匹配,在我们这个不匹配的字符前面,所有的字符全部都匹配
疑问:
为什么不从中间开始匹配?
如果中间有和后缀一样的,但是开头没有呢?
我们要求必须从开头开始算前缀是不是容易漏掉一些可能的正确答案呢?
答案:
如果中间有,但是开头不行,那最后子串中间的字符是匹配了,但是中间的 前面的那部分字符终究还是不匹配,那迟早要完蛋,终究是失败的
另外
为了强调和体现我们“求的是next【j+1】”的思想和精神,我们不妨把【if(1)】处的代码更新为:(虽然我后面最后的答案里面不是这么写的)
if (k == -1 || T.ch[k] == T.ch[j]){next[j + 1] = k + 1;j++;k++;}
答案:
#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit
#include<math.h>//OVERFLOW,exittypedef int Status;
#define MAXLEN 255struct SString//Sequence String
{char ch[MAXLEN + 1]; //存储串的一维数组int length; //串的当前长度长度
};void Get_next(SString T, int *next)
//给你一个子串T,教你逐个算出每个位序对应的next[]
{int j = 0,//从头开始算起k = -1;next[0] = -1;//根据公式while (j <= T.length - 1)//因为位序从0(而非1)开始{if (k == -1 || T.ch[k] == T.ch[j]){j++;k++;next[j] = k;}elsek = next[k];}
}int Index_KMP(SString S, SString T, int pos)
{int next[MAXLEN];Get_next(T, next);int i = pos, j = 1;while (i <= S.length && j <= T.length){if (S.ch[i] == T.ch[j]){++i; ++j;}//主串和子串依次匹配下一个字符elsej = next[j];}if (j > T.length)return i - T.length; //匹配成功elsereturn 0;
}int main()
{}
相关文章:

数据结构与算法基础(王卓)(17):KMP算法详解(精讲(最简单、直接、有效的思路方法,答案以及代码原理)
本文具体思路参考: (最后证明,该教材/网课实际上是最有效的) DS第四章【3】KMP1_哔哩哔哩_bilibili 中间走的一些弯路的教材: 第06周05--第4章串、数组和广义表5-4.3串的操作--串的匹配算法2--KMP算法_哔哩哔哩_bi…...

【java基础】HashMap源码解析
文章目录基础说明构造器put方法(无扩容,无冲突)put方法(无冲突,有扩容)put方法(有冲突,无树化)put方法(有冲突,树化)remove方法&#…...
实现异步的8种方式,你知道几个?
一、前言 在编程中,有时候我们需要处理一些费时的操作,比如网络请求、文件读写、数据库操作等等,这些操作会阻塞线程,等待结果返回。为了避免阻塞线程、提高程序的并发处理能力,我们常常采用异步编程。 异步编程是一种…...
二叉树的三种遍历
二叉树的遍历可以有:先序遍历、中序遍历、后序遍历先序遍历:根、左子树,右子树中序遍历:左子树、根、右子树后序遍历:左子树、右子树、根下面是我画图理解三种遍历:二叉树里都是分为左子树和右子树。分治思…...

我,30岁程序员被裁了,千万别干全栈
大家好,这里是程序员晚枫,今天是读者投稿。下面开始我们的正文。👇 关注博主👉程序员晚枫 很久了,今天给大家分享一下我从事程序员后,30岁被裁的经历,希望帮到有需要的人。 1、我被裁了 大家好…...

【linux】:进程地址空间
文章目录 前言一、进程地址空间总结前言 本篇文章接着上一篇文章继续讲解进程,主要讲述了进程在运行过程中是如何在内存中被读取的以及为什么要有虚拟地址的存在,CPU在运行过程中是拿到程序的虚拟地址还是真实的物理内存。 一、进程地址空间 下面我们先…...

【保姆级】JMeter Mqtt 压测配置
忽然有个紧急任务要对某个服务做MQTT做压测,紧急实操下JMeter,这里记录下非专业测试员的测试过程、(▽`),欢迎👏大家检查指点( ̄∇ ̄)/下载⏬工具JMeter官方下载地址https://jmeter.apache.org/do…...

C语言数据结构初阶(4)----带头双向循环链表
我们先来看看带头双向循环链表的结构:看到这里我们可能会产生一个想法:这个链表看起来好复杂的样子,是不是它的增删改查比单链表更难写呢?嘿嘿,还真的不是这样的,双向链表的增删改查是很好写的哦࿰…...

原生javascript手写一个丝滑的轮播图
通过本文,你将学到: htmlcssjs 没错,就是html,css,js,现在是框架盛行的时代,所以很少会有人在意原生三件套,通过本文实现一个丝滑的轮播图,带你重温html,css和js基础知识。 为什么选用轮播图做示例&…...

【Linux】进程优先级(进程优先级 Linux下优先级 用top命令更改已存在进程的nice 其他概念 进程切换)
文章目录进程优先级Linux下优先级用top命令更改已存在进程的nice:其他概念进程切换进程优先级 我们作为使用者一般不关心优先级,它跟我们的调度器有很大的关系,调度器是为了跟均衡的调度进程。 什么叫做优先级? 优先级和权限是两…...

2016年chatGPT之父Altman与马斯克的深度对话(值得一看)
2016年9月,现今OpenAI CEO,ChatGPT之父,时任创投公司Y Combinator的总裁Sam Altman在特斯拉加州弗里蒙特工厂采访了埃隆马斯克。马斯克阐述了创建OpenAI的初衷,以及就他而言,对于未来最为重要的五件事。这是OpenAI的两…...

基于vscode开发vue3项目的详细步骤教程 3 前端路由vue-router
1、Vue下载安装步骤的详细教程(亲测有效) 1_水w的博客-CSDN博客 2、Vue下载安装步骤的详细教程(亲测有效) 2 安装与创建默认项目_水w的博客-CSDN博客 3、基于vscode开发vue项目的详细步骤教程_水w的博客-CSDN博客 4、基于vscode开发vue项目的详细步骤教程 2 第三方图标库FontAw…...

【C语言】每日刷题 —— 牛客语法篇(5)
前言 大家好,继续更新专栏 c_牛客,不出意外的话每天更新十道题,难度也是从易到难,自己复习的同时也希望能帮助到大家,题目答案会根据我所学到的知识提供最优解。 🏡个人主页:悲伤的猪大肠9的博…...

操作系统(2.1)--进程的描述与控制
目录 一、前驱图和程序执行 1.前驱图 2.程序顺序执行 2.1 程序的顺序执行 2.2 程序顺序执行时的特征 3. 程序并发执行 3.1程序的并发执行 3.2 程序并发执行时的特征 一、前驱图和程序执行 1.前驱图 前趋图:是一个有向无循环图,用于描述进程之间执行的前后…...
JAVA查看动态代理类
JAVA查看代理类 1. 代理类 class 生成 System.setProperty // jdk8及之前的设置System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");// or System.getProperties().put("sun.misc.ProxyGenerator.saveGenerated…...

Chapter2 : SpringBoot配置
尚硅谷SpringBoot顶尖教程 1. 全局配置文件 SpringBoot使用一个全局的配置文件 application.properties 或者 application.yml ,该配置文件放在src/main/resources目录或者类路径/config目录下面, 可以用来修改SpringBoot自动配置的默认值。 yml是YA…...

手撕单链表练习
Topic 1:LeetCode——203. 移除链表元素203. 移除链表元素 - 力扣(LeetCode)移除链表中的数字6操作很简单,我们只需要把2的指向地址修改就好了,原来的指向地址是6现在改为3这个思路是完全正确的,但是在链表…...

Kubuntu安装教程
文章目录Kubuntu介绍下载Kubuntu在VMware虚拟机中安装Kubuntu1. 点击“创建新的虚拟机”2. 选择“自定义(高级)”3. 按照下图所示进行设置设置网络4. 点击“自定义硬件”5. 开启虚拟机6. 进入安装界面,选择中文,之后点击“安装Kub…...

[蓝桥杯] 树状数组与线段树问题(C/C++)
文章目录 一、动态求连续区间和 1、1 题目描述 1、2 题解关键思路与解答 二、数星星 2、1 题目描述 2、2 题解关键思路与解答 三、数列区间最大值 3、1 题目描述 3、2 题解关键思路与解答 标题:树状数组与线段树问题 作者:Ggggggtm 寄语:与其…...
Matlab-Loma Prieta 地震分析
此示例说明如何将带时间戳的地震数据存储在时间表中以及如何使用时间表函数来分析和可视化数据。 加载地震数据 示例文件quake.mat包含 1989 年 10 月 17 日圣克鲁斯山脉 Loma Prieta 地震的 200 Hz 数据。这些数据由加州大学圣克鲁斯分校查尔斯F里希特地震实验室的 Joel Yelli…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...