超简单理解KMP算法(最长公共前后缀next数组、合并主子串、子串偏移法)
KMP算法理解
- 最长公共前后缀next
- 合并主子串
- 子串偏移
参考b站:子串偏移、合并主子串
最长公共前后缀next
这个概念是一个trick,帮助我们记录遍历了一遍的数组的相似特性,想出来确实很nb,我也不理解逻辑是怎么想出来的。
字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
例如,对于字符串 "ababc":
- 前缀有:
"a","ab","aba","abab",第一个字符一定会有 - 后缀有:
"c","bc","abc","babc",最后一个字符一定会有
最长公共前后缀:指某个字符串的前缀和后缀中相等的最长子串。例如:
- 对于
"abab",最长公共前后缀是"ab",长度为 2。 - 对于
"abc",没有公共前后缀,长度为 0。
例如,对于模式串 "ababc",其前缀表为 [0, 0, 1, 2, 0]:
- 第 0 位:
"a",没有前后缀,值为 0。 - 第 1 位:
"ab",没有公共前后缀,值为 0。 - 第 2 位:
"aba",最长公共前后缀为"a",值为 1。 - 第 3 位:
"abab",最长公共前后缀为"ab",值为 2。 - 第 4 位:
"ababc",没有公共前后缀,值为 0。
现在我们希望在一次遍历字符串的过程中记录该字符串的相似性质,该如何计算最长公共前后缀的数组,通常被称为next?用递推的性质:
假设我们希望求next[i],并知道红色段next[i-1]=len,那说明[0,i-1]的子串的首尾均有长度为len的前后缀
- 如果str[i]==str[len],说明[0,i]的子串的首尾均有len+1长度的前后缀,得到next[i] =len+1(注意下标从0开始所以比较的是str[len])

- 如果str[i]!=str[len],我们继续寻找[0,i-1]的子串中第二长、第三长的前缀串粉色段,这些字符串虽然长度小于len,但起点和终点依然是0和j-1,因为next[i]找到的前后缀一定要经过0和j

- 把寻找的过程可以写成while,目标是找到尽量长的粉色段n,并满足str[n]=str[i]
- 现在问题是n怎么获得?我们最初知道next[i-1]=len,所以绿色=橙色,而现在又找到了蓝色=橙色,所以有蓝色=绿色,所以粉色段恰恰可以用next[len-1]表示,当找到了粉色段但不满足str[n]=str[i]时,继续找下一个粉色段,即next[n-1],如此循环

- 最终可以得到next数组,方法如下:
vector<int> pi(str.size());
for (int i = 1; i < str.size(); i++) {int len = pi[i-1];while (len != 0 && str[i] != str[len]) {len = pi[len - 1];}if (str[i] == str[len]) {pi[i] = len + 1;}
}
合并主子串
28. 找出字符串中第一个匹配项的下标
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
把haystack 和 needle合并,中间用#分隔,求合并串的next数组,如果数组中有值等于len(needle),则说明有匹配项

这种方法可以直接在next数组获得的过程中判断,代码如下:
class Solution:def strStr(self, haystack: str, needle: str) -> int:n = len(haystack) #主串的的长度m = len(needle) #子串的长度if haystack == needle: #因为i从1开始,所以处理edge casereturn 0s = needle + "#" + haystack #注意把子串放前面,这样前缀和才能覆盖子串next = [0] * len(s)for i in range(1, n + m + 1): #注意+1是因为"#"多了一位len_ = next[i - 1]while len_ != 0 and s[i] != s[len_]: #等于0也是没有,会跳过的len_ = next[len_ - 1]if s[i] == s[len_]: #跳出循环要不是0要不相等next[i] = len_ + 1if next[i] == m:#返回第一个return i - m*2 #合并串从第m+1位开始才是主串,所以主串中开始匹配的下标是i - (m+1) - m + 1=i-2*mreturn -1
子串偏移
KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
子串偏移方法一句话就是利用子串的前后缀和,保证下一个可能匹配的地方就是主串的后缀。
- 比如第i位AC虽然没有匹配上,但我们知道[0,i-1]的内容是一样的,如何复用这一信息?注意i指的是子串指针,这里图示主串和子串指针是一样的所以没有区分,但本质要利用子串next数组的前后缀相同,所以要用子串指针

- 为了和暴力方法区分,我们不希望直接把主串的指针移到下一个位置开始遍历,而是一直向前方移动,这需要我们移动指针到仍然有可能重新匹配的地方,如何寻找?利用next数组和[0,i-1]的内容的已知信息
- [0,i-1]中的前后缀长度是next[i-1],而主串匹配子串最基本的要求就是从第一个字符相等。现在主串的头需要移动(因为0作为起点不行了),我们又不像只移动一个,这时可以想到主串的后缀恰好等于子串的开头,举个例子
- 主串是ABCABDC,子串是ABCABC,i此时是5,我们已知[0,4]的ABCAB相等和next[4]=2。主串把0当起点时找不到匹配的,但此时主串[0,4]的末尾和子串的开头相等,都是AB,所以把主串的起点移动到i-next[i-1]=5-2=3后,主串和子串又有了next[i-1]长度的相等段(因为next数组的定义,0-3之间不可能有另外等于子串开头的部分,前后缀一定会覆盖到第一个可能的起点处,如ABACAB匹配ABACAD时,不可能把起点移到中间的A,因为下一位是C,前后缀在计算时就比较过了)
- 这样等价于主串不动,还是从第i位开始比较,而子串从则回退到next[i-1]位,因为子串的[0,i-1)已知和主串是相等的,回到图中的例子就是:


这种方法的代码如下:
class Solution:def getNext(self, next, s):for i in range(1, len(s)):len_ = next[i - 1]while len_ != 0 and s[i] != s[len_]: #等于0也是没有,会跳过的len_ = next[len_ - 1]if s[i] == s[len_]: #跳出循环要不是0要不相等next[i] = len_ + 1def strStr(self, haystack: str, needle: str) -> int:next = [0] * len(needle)self.getNext(next, needle)i = 0 #主串指针j = 0 #子串指针for i in range(len(haystack)):while j > 0 and haystack[i] != needle[j]:#不匹配,子串到下一个可能匹配的地方next[j-1],注意只要j>0要一直找,而不是只试一次j = next[j - 1]if haystack[i] == needle[j]:#字符匹配,指针后移j += 1if j == len(needle):#在主串中找到了子串return i - len(needle) + 1return -1
相关文章:
超简单理解KMP算法(最长公共前后缀next数组、合并主子串、子串偏移法)
KMP算法理解 最长公共前后缀next合并主子串子串偏移 参考b站:子串偏移、合并主子串 最长公共前后缀next 这个概念是一个trick,帮助我们记录遍历了一遍的数组的相似特性,想出来确实很nb,我也不理解逻辑是怎么想出来的。 字符串的…...
【每日论文】TESS 2: A Large-Scale Generalist Diffusion Language Model
下载PDF或阅读论文,请点击:LlamaFactory - huggingface daily paper - 每日论文解读 | LlamaFactory | LlamaFactory 摘要 我们推出了TESS 2,这是一种通用的指令跟随扩散语言模型,其性能优于当代的指令调整扩散模型,有…...
如何在 React 中测试高阶组件?
在 React 中测试高阶组件可以采用多种策略,以下是常见的测试方法: 1. 测试高阶组件返回的组件 高阶组件本身是一个函数,它返回一个新的组件。因此,可以通过测试这个返回的组件来间接测试高阶组件的功能。通常使用 Jest 作为测试…...
设计模式学习笔记
说了一万遍!学习要做笔记! 时间一长,就会忘了,后面再来学,又要从头学起 关键是重难点!!!当初学的时候就是因为攻克难点、寻找重点花费时间 不做笔记每次复习都要浪费时间在重难点上…...
写论文技巧 :Word文档插入图片,实现自动对齐
插入表格,调整大小 取消自动适应 插入图片,去掉边框...
VSCode - VSCode 切换自动换行
VSCode 自动换行 1、基本介绍 在 VSCode 中,启用自动换行可以让长行代码自动折行显示,避免水平滚动条频繁使用,提升代码阅读体验 如果禁用自动换行,长行代码就需要手动结合水平滚动条来阅读 2、演示 启用自动换行 禁用自动换…...
postman传query一个数组类型的参数,并且数组里面只有一个值的时候
1.在所加的检索项目后面加上[0], 例: item[0]2.数组里面多个值的时候,写两个相同的项目名,值不相同 itemvalue1 itemvalue2再看不懂,我也没办法了。...
【智能客服】ChatGPT大模型话术优化落地方案
本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 一、项目背景 1.1 行业背景 1.2 业务现…...
vue3 文件类型传Form Data数据格式给后端
在 Vue 3 中,如果你想将文件(例如上传的 Excel 文件)以 FormData 格式发送到后端,可以通过以下步骤实现。这种方式通常用于处理文件上传,因为它可以将文件和其他数据一起发送到服务器。 首先,创建一个 Vue…...
高考或者单招考试需要考物理这科目
问题:帮忙搜索一下以上学校哪些高考或者单招考试需要考物理这科目的 回答: 根据目前获取的资料,明确提及高考或单招考试需考物理的学校为湖南工业职业技术学院,在部分专业单招时要求选考物理;其他学校暂未发现明确提…...
深入剖析 DeepSeek:张量计算范式全解析
一、引言 在 AI 技术迅猛发展的当下,DeepSeek 以其卓越的性能成为研究热点。清华大学的《DeepSeek:从入门到精通》这一珍贵资料,为我们深入挖掘 DeepSeek 核心原理提供了指引,其中张量计算范式更是关键所在,它构建起整…...
VSCode集成deepseek使用介绍(Visual Studio Code)
VSCode集成deepseek使用介绍(Visual Studio Code) 1. 简介 随着AI辅助编程工具的快速发展,VSCode作为一款轻量级、高度可扩展的代码编辑器,已成为开发者首选的工具之一。DeepSeek作为AI模型,结合Roo Code插件&#x…...
【保姆级教程】DeepSeek R1+RAG,基于开源三件套10分钟构建本地AI知识库
一、总体方案 目前在使用 DeepSeek 在线环境时,页面经常显示“服务器繁忙,请稍后再试”,以 DeepSeek R1 现在的火爆程度,这个状况可能还会持续一段时间,所以这里给大家提供了 DeepSeek R1 RAG 的本地部署方案。最后实现…...
vue,vue3 keepalive没有效果,无法缓存页面include无效,keep-alive
keepalive没有效果,无法缓存页面? 问题大概是组件的name值不对应,vue2修改组件文件的name值,vue3保持组件文件名称和路由页面配置的name一致就可以了,如果vue3不想保持一致,必须手动在文件后面添加export..…...
Windows逆向工程入门之指针类型
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 1. 指针特性 1.1 指针的优点 1.2 指针的缺点 2. 智能指针 2.1 智能指针的优点 2.2 智能指针的缺点 3. 指针的安全攻防 3.1 指针使用 3.2 指针运算 3.3 指针引用 3.4 参数传递 …...
PHP+Apache+MySQL安装(Windows)
一、安装教程 参考链接1 参考链接2 二、问题描述 PHP安装目录下找不到php8apache2_4.dll PHP安装包下载错误 Apache Service Monitor: request operation has failed! 定位问题: 查看【事件查看器】 解决问题 安装或更新与PHP版本相对应的Visual C Redistribu…...
算法基础 -- 堆排序之C语言实现
C语言实现堆排序(Heap Sort) 1. 代码实现 下面是 C语言实现的堆排序接口,支持 通用数据类型排序,并采用 函数指针 进行 自定义比较,适用于 整数排序 或 结构体排序。 完整代码 大根堆 #include <stdio.h> #…...
Hutool - Extra:功能丰富的扩展模块
一、简介 Hutool - Extra 作为 Hutool 工具包的扩展模块,对众多第三方库和功能进行了封装,极大地丰富了 Hutool 的功能体系。它涵盖了模板引擎、邮件发送、Servlet 处理、二维码生成、Emoji 处理、FTP 操作以及分词等多个方面,为开发者在不同…...
C++ 中的继承详解(上)
目录 1、继承的概念及定义 1.1、继承的概念 1.2、继承定义 1.2.1、定义格式 1.2.2、继承方式 1.2.3、继承基类成员访问方式的变化 2、基类和派生类对象赋值转换 3、继承中的作用域 4、派生类的默认成员函数 补充:封装的层次(实际上有很多层的,这…...
halcon三维点云数据处理(二十五)moments_object_model_3d
目录 一、moments_object_model_3d例程二、moments_object_model_3d函数三、效果图 一、moments_object_model_3d例程 这个例子说明了如何使用moments_object_model_3d运算符来将3D数据与x、y、z坐标轴对齐。在实际应用中,通过3D传感器获取的物体模型可能具有一个与…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
