字符串匹配究极大招【KMP】:带你一步步从原理到构建
前言
一文带你了解如何去理解并实现KMP算法。本文用于记录自己的学习过程,同时向大家进行分享相关的内容。本文内容参考于 代码随想录 同时包含了自己的许多学习思考过程,如果有错误的地方欢迎批评指正!
KMP原理
首先来知道什么是KMP,KMP是由三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP。其主要是应用在字符串匹配上面的。KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。那么通过什么方式可以利用已经匹配的信息呢,这时候就需要next数组了。next数组本质上就是一个前缀表。
什么是前缀表
在弄清楚什么是前缀表的时候,我们得先知道什么是前缀、后缀。前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
举个例子来说哈,对于字符串abcdfe来说,abc子串的所有前缀为a和ab,所有后缀为b和bc
那么前缀表的作用是什么呢?前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。如图所示,当b和f不匹配的时候,不会从头开始,而是会跳到b来进行匹配。所以到这我们就可以知道了什么是前缀表:即记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀

如何构建前缀表
长度为前1个字符的子串a,最长相同前后缀的长度为0。(注意字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。)因为其没有前后缀,所以第一个字符必定为0.

长度为前2个字符的子串aa,最长相同前后缀的长度为1。其前缀a和其后缀a最长相等为1

长度为前3个字符的子串aab,最长相同前后缀的长度为0。无最长相等的前后缀

以此类推: 长度为前4个字符的子串aaba,最长相同前后缀的长度为1。 长度为前5个字符的子串aabaa,最长相同前后缀的长度为2。 长度为前6个字符的子串aabaaf,最长相同前后缀的长度为0。
那么把求得的最长相同前后缀的长度就是对应前缀表的元素,如图:

可以看出模式串与前缀表对应位置的数字表示的就是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
next数组
说到这,我们就得回到next数组了。那么做字符串匹配的时候,很多时候都是以next数组进行回退的,那么next数组与前缀表又是什么关系呢?其实本质上next数组就是前缀表,当然有些人对前缀表有不同的处理,所以next数组的形式可能会有不同,但其本质上都是一样的,逻辑都是相通的。这里直接讲述让前缀表作为next数组(别的处理有全部减一或者整体向右边移动一位)。
这里我们来讲述如何构建next数组。(以python代码为例)
构建next数组有三个重要步骤:
- 初始化
- 处理前后缀不相同的情况
- 处理前后缀相同的情况
初始化:初始化的不同,决定了你的next数组的形式。我们这里将定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。我们这里将j初始化为0,并且将next数组的第一位初始化为0.
处理前后缀不相同的情况:因为j初始化为0,那么i就从1开始,进行s[i] 与 s[j]的比较。所以遍历模式串s的循环下标i 要从 1开始,如果 s[i] 与 s[j]不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回退。怎么回退呢?next[j]就是记录着j(包括j)之前的子串的相同前后缀的长度。那么 s[i] 与 s[j] 不相同,就要找 j前一个元素在next数组里的值(就是next[j-1])。
处理前后缀相同的情况:如果 s[i] 与 s[j ] 相同,那么就同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。
最后其整体代码如下:
def getNext(self, next: List[int], s: str) -> None:
j = 0
next[0] = 0
for i in range(1, len(s)):
while j > 0 and s[i] != s[j]:
j = next[j - 1]
if s[i] == s[j]:
j += 1
next[i] = j
使用next数组做匹配
好了,最重要的来了,前面讲述了那么多,其根本就是为了字符串匹配做工作。我们要在文本串s里 找是否出现过模式串t。以下是具体的思路及其步骤。
首先定义两个下标j 指向模式串起始位置,i指向文本串起始位置。
那么j初始值依然为0,为什么呢? 依然因为next数组里记录的起始位置为0。
i就从0开始,遍历文本串。
接下来就是 s[i] 与 t[j ] (因为j从0开始的) 进行比较。
如果 s[i] 与 t[j ] 不相同,j就要从next数组里寻找下一个匹配的位置。
如果 s[i] 与 t[j + 1] 相同,那么i 和 j 同时向后移动.
如何判断在文本串s里出现了模式串t呢,如果j指向了模式串t的末尾,那么就说明模式串t完全匹配文本串s里的某个子串了。
要在文本串字符串中找出模式串出现的第一个位置 (从0开始),所以返回当前在文本串匹配模式串的位置i 减去 模式串的长度,就是文本串字符串中出现模式串的第一个位置。
并且我们来看其时间复杂度,用暴力法解题即两个for循环,时间复杂度为O($n^2$),而用KMP算法来解题,其时间复杂度为O($m+n$),其m和n分别为字符串s和模式串t的长度,通常模式串t不会很大,所以一般是可以忽略的。
所以最后完整的代码为:
def strStr(self, haystack: str, needle: str) -> int:
if len(needle) == 0:
return 0
next = [0] * len(needle)
self.getNext(next, needle)
j = 0
for i in range(len(haystack)):
while j > 0 and haystack[i] != needle[j]:
j = next[j - 1]
if haystack[i] == needle[j]:
j += 1
if j == len(needle):
return i - len(needle) + 1
return -1
实战演练
28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

class Solution:
def getNext(self, next: List[int], s: str) -> None:
j = 0
next[0] = 0
for i in range(1, len(s)):
while j > 0 and s[i] != s[j]:
j = next[j - 1]
if s[i] == s[j]:
j += 1
next[i] = j
def strStr(self, haystack: str, needle: str) -> int:
if len(needle) == 0:
return 0
next = [0] * len(needle)
self.getNext(next, needle)
j = 0
for i in range(len(haystack)):
while j > 0 and haystack[i] != needle[j]:
j = next[j - 1]
if haystack[i] == needle[j]:
j += 1
if j == len(needle):
return i - len(needle) + 1
return -1
字符串匹配究极大招【KMP】:带你一步步从原理到构建的更多相关文章
- sdut 2125串结构练习--字符串匹配【两种KMP算法】
串结构练习——字符串匹配 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目链接:http://acm.sdut.edu.cn/sduto ...
- 字符串匹配-BF算法和KMP算法
声明:图片及内容基于https://www.bilibili.com/video/av95949609 BF算法 原理分析 Brute Force 暴力算法 用来在主串中查找模式串是否存以及出现位置 ...
- 洛谷 P3375 【模板】KMP字符串匹配 || HDU 1686 Oulipo || kmp
HDU-1686 P3375 kmp介绍: http://www.matrix67.com/blog/archives/115 http://www.cnblogs.com/SYCstudio/p/7 ...
- 字符串KMP——用途广泛的字符串匹配算法 + 扩展KMP——特殊定义的字符串匹配
引 入 引入 引入 " SY 和 WYX 在看毛片.(几 毛 钱买到的动作 片,毛 片) WYX 突然想回味一个片段,但是只记得台词里面有一句挺长的 " ∗ ∗ ∗ ∗ **** ...
- KMP字符串匹配 简单理解
http://www.cnblogs.com/c-cloud/p/3224788.html 字符串匹配,长串长度为m,子串长度为n 则,暴力破解的复杂度为o(m*n) 如果用kmp匹配,则复杂度为o( ...
- zstu.4194: 字符串匹配(kmp入门题&& 心得)
4194: 字符串匹配 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 206 Solved: 78 Description 给你两个字符串A,B,请 ...
- 字符串匹配KMP算法详解
1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...
- 字符串匹配的KMP算法
~~~摘录 来源:阮一峰~~~ 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串”BBC ABCDAB ABCDABCDABDE”,我想知道,里面是否包含另一个字符串”ABCDABD”? 许 ...
- {Reship}{KMP字符串匹配}
关于KMP字符串匹配的介绍和归纳,作者的思路非常清晰,推荐看一下 http://blog.csdn.net/v_july_v/article/details/7041827
- 字符串匹配的KMP算法详解及C#实现
字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...
随机推荐
- 深入浅出:Agent如何调用工具——从OpenAI Function Call到CrewAI框架
深入浅出:Agent如何调用工具--从OpenAI Function Call到CrewAI框架 嗨,大家好!作为一个喜欢折腾AI新技术的算法攻城狮,最近又学习了一些Agent工作流调用工具的文章,学 ...
- linux 亲测wget安装7.3 liferay流程
liferay wget 7.3版本安装1. 下载软件包 sudo wget https://sourceforge.net/projects/lportal/files/Liferay%20Port ...
- 【T+重要提示】日期不在业务期间范围内
2021年就来了,今天是新年的第二个工作日.相信很多会计朋友们在打开T+软件的时候,会看到这么个提示框: 用友T+2020年没有做完账务,提前建立2021年度帐的步骤 (1)用账套主管选择2020年1 ...
- (vm/vb)虚拟机复制或者拷贝之后连不上网络怎么处理?
(vm/vb)虚拟机复制或者拷贝之后连不上网络怎么处理? Linux虚拟机无论在VMware还是VirtualBox下面,只要复制拷贝到别的地方,开启网络服务都会出现报错的问题. 这里以CentOS ...
- 天翼云存储资源盘活系统HBlock,全面释放企业数据价值
9月6日,天翼云与科技媒体InfoQ联合举办的以"存储难题新解法,揭秘极/致易用的HBlock"为主题的线上技术分享会圆满落幕.天翼云国际业务事业部研发专家武志民与存储产品线总监魏 ...
- Luogu P4310 绝世好题 题解 [ 绿 ] [ 线性 dp ] [ 单调队列优化 ] [ 二进制优化 ]
题目:绝世好题. 暴力 dp 显然 \(O(n^2)\) 转移即可. 单调队列优化 观察到只有某二进制位两个数都为 \(1\) 时才能转移,因此我们把每个二进制位开一个单调队列,然后对于一个数 \(a ...
- 清华博士后的DeepSeek使用手册,104页,真的是太厉害了!(免费领取源文件)
<DeepSeek从入门到精通2025>是由清华大学元宇宙文化实验室的余梦珑博士后及其团队撰写.文档的核心内容围绕DeepSeek的技术特点.应用场景.使用方法以及如何通过提示语设计提升A ...
- 查看 OceanBase 执行计划
使用benchmarksql压测数据库,产生高消耗的sql并测试数据库性能 压测环境部署 benchmarksql下载 git clone https://github.com/meiq4096/be ...
- MarkDown学习使用图片
学习MarkDown使用
- KUKA库卡机器人维修
KUKA库卡机器人作为生产线上的核心设备,一旦出现KUKA机械手故障,将直接影响整个生产线的运行效率.及时的库卡机器人维修工作不仅能够迅速恢复机器人的工作状态,减少生产停滞时间,还能通过预防性维护降低 ...