数据结构字符串匹配KMP算法的详解(题目讲解 简单易懂)

简介: 数据结构字符串匹配KMP算法的详解(题目讲解 简单易懂)

有问题欢迎评论区私信留言交流~~~

博主近来在复习数据结构的过程中遇到了KMP字符串匹配算法,在浏览了网上众多文章后感觉写的不够清晰和简单易懂,尤其是从做题的角度上来讲,下面就个人对KMP算法的理解进行解题,有问题还请谅解~

首先我们来看一下KMP算法的定义

KMP算法定义

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)

KMP算法是三位学者在 Brute-Force算法的基础上同时提出的模式匹配的改进算法。Brute- Force算法在模式串中有多个字符和主串中的若干个连续字符比较都相等,但最后一个字符比较不相等时,主串的比较位置需要回退。KMP算法在上述情况下,主串位置不需要回退,从而可以大大提高效率

所以我们可以明确KMP算法的核心就是当一位字符串匹配失败后子串应该移动的距离,所以关键是要求出这个移动的距离,明确了我们的目标之后下面我们以具体的题目进行讲解

基本概念

首先我们明确几个题目中要用到的基本概念

1:主串 就是要被匹配的串

2:字串 也称模式串,用于匹配主串

3:前缀

4:后缀

5:最长相等前后缀长度

6:部分匹配值PM

7:next数组

8:nextval数组

想必大家看到上面如此多的概念难免头晕犯难,下面我们以具体的题目进行讲解,这个算法实际上非常好理解

前缀指的是除最后一个字符外,字符串的所有头部字串。后缀指的是除第一个字符外,字符串的所有尾部子串,部分匹配值即为字符串的前缀和后缀的最长相等前后缀长度

下面我们从一个具体的例子来求解以上概念

按照规则一个个求前后缀即可,这里有一个小坑就是后缀是从往前的,所以是ba bab...

那么上面的部分匹配值表中某个字符不匹配时子串需要向后移动的位数计算公式如下

移动位数=已匹配的字符数-对应字符的部分匹配值

切记,此处查找的是最后一个匹配字符即下图b的部分匹配值

方法如下图所示,其余字符不匹配方法相同,直至字符串匹配成功或匹配失败为止

这里写错了 应该是b的部分匹配值为0

那么问题来了,既然上面已经可以计算出移动位数,那么next和nextval数组又是什么呢?

因为上面的计算方法还是不够直观和简单,还要查找上一个字符的部分匹配值,所以我们进一步简化,将PM表整体右移一位,就得到了next数组,第一个元素右移后空缺用-1填充,最后一个元素右移溢出不用理会直接舍去,有时为了计算方便也可以next数组值整体加1,这点应根据题目干条件灵活应对,其实串为序从1开始就整体+1,从0开始则不用加

此时移动位数=已匹配字符数-对应部分的next数组值(即不用看上一位的部分匹配值)

所以上图中的next数组为-1 0 0 01 如果加1则为 0 1 1 1 2读者可自行验证

最后nextval数组就是对KMP算法的进一步优化,失配时如果Pj=Pnext[j]则会匹配相等的字符,解决办法是递归计算next[j]并将更新后的next[j]命名为nextval[j],递归计算方法为

nextval[j]=next[next[j]]

如果递归一次后仍然相等则继续递归,直至不等即可

创作不易 觉得有帮助请点赞关注收藏~~~

目录
打赏
0
0
0
0
193
分享
相关文章
局域网行为监控软件 C# 多线程数据包捕获算法:基于 KMP 模式匹配的内容分析优化方案探索
本文探讨了一种结合KMP算法的多线程数据包捕获与分析方案,用于局域网行为监控。通过C#实现,该系统可高效检测敏感内容、管理URL访问、分析协议及审计日志。实验表明,相较于传统算法,KMP在处理大规模网络流量时效率显著提升。未来可在算法优化、多模式匹配及机器学习等领域进一步研究。
46 0
|
9月前
|
两个字符串匹配出最长公共子序列算法
本文介绍了最长公共子序列(LCS)问题的算法实现,通过动态规划方法求解两个字符串的最长公共子序列,并提供了具体的编程实现细节和示例。
193 1
两个字符串匹配出最长公共子序列算法
|
9月前
|
栈和队列题目练习
栈和队列题目练习
76 0
|
9月前
|
第四章 KMP算法理论基础
第四章 KMP算法理论基础
107 0
|
9月前
|
KMP算法
KMP算法
83 0
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
8月前
|
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
186 59
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
24 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等