数据结构--KMP算法

news/2024/4/29 19:35:37

数据结构–KMP算法

首先我在这里提出以下问题,一会一起进行探讨

1.什么是最长公共前后缀
2. KMP算法怎么实现对匹配原理
3. 最长公共前后缀怎么求解

KMP算法可以用来解决什么问题?
答:在字符串中匹配子串,也称为模式匹配

分析

什么是最长公共前后缀?

通俗来说,就是一个字符串开头到某一个位置与其某一个位置到字符串结束一模一样,即 P0…PK-1 与 Pi-k…Pi-1的字符串相同,注意我们这里的最长公共前后缀是指的真串,即不包含该字母,如对于字符串 aba,其前缀有 a,ab,其后缀有ba,a那么最长公共公共前后缀为a
下面是实例:
对于ababcabababe
我们手动遍历一次从 i = 0开始
i = 0,字符串为 a
因为不包含本身,所以其无前后缀,那么自然也没有公共前后缀

i = 1,字符串为 ab
其 前缀为 a,后缀为 b,无公共前后缀

i = 2,字符串为 aba
其 前缀有 a,ab 后缀有 ba,a 公共前后缀为a

i = 3,字符串为 abab
其前缀为 a,ab,aba 其后缀有bab,ab,b最长公共前后缀为ab

i = 4,字符串为 ababc
其前缀为 a,ab,aba,abab 其后缀有babc,abc,bc,c 所以无公共前后缀

同理,下面的我们肉眼观察(开头有没有一段字符串和结尾一段字符串相同)

i = 5,字符串为 ababca
其最长公共前后缀为 a

i = 6,字符串为 ababcab
其最长公共前后缀为 ab

i = 7,字符串为 ababcaba
其最长公共前后缀为 aba

i = 8,字符串为 ababcabab
其最长公共前后缀为 abab

i = 9,字符串为 ababcababa
其最长公共前后缀为 aba

i = 10,字符串为 ababcababab
其最长公共前后缀为 abab

i = 11,字符串为 ababcabababe
其无公共前后缀

对于 i 遍历到每个元素,我们可以创建一个Next[ ]数组来保存该元素之前的所有字符形成的字符串的最长公共前后缀

那么对于任意字符串,第一个元素没有前面的元素,我们则把其的Next置位-1,即Next[ 0 ] = -1;而对于第一个元素,我们知道只有一个元素时,其永远没有不包含本身的前后缀,所以第二个元素保存的该元素之前的所有字符形成的字符串的最长公共前后缀永远为0,即永远Next[ 1 ] = 0;
对于这样保存的方法,我们有下图
在这里插入图片描述
那么问题是怎么求解Next[ ] 数组

Next[ ]数组求解(最长公共前后缀怎么求解)

完成从上面的理论分析到代码实现,我们需要两个"指针",因为最长公共前后缀本质上前面的一段字符串等于最后的一段字符串,那么,我们用 i 来完成对后面字符串的遍历,而 用 k 来完成对前面字符串的遍历(看不懂别急,下面还有解释)
代码:

public void getNext(int [] Next,String s){int i = 0;// 遍历字符串的后端int k = -1;// 遍历字符串的前端Next[0] = -1;// 初始第一个为-1while (i < s.length()){if (k == -1 || s.charAt(i) == s.charAt(k)){// 如果前面没有公共前后缀时,k为-1,那么其下一个索引元素储存的就为++k(就为0)// 如果前面的字符串有最长公共前后缀,且在该元素(后面字符串的最后一位)等于前面字符串(存储的前面字符串的公共前缀)的下一位时// 那么下一个索引元素(++i)储存的就为++kNext[++i] = ++k;}else {// 如果该元素(后面字符串的最后一位)不等于前面字符串(存储的前面字符串的公共前缀)的下一位// 令 k = Next[k]得到其前面的字符串的公共前缀的下一位(不懂可以看图)k = Next[k];}}}

对于字符串ababcabababe
这里举几个特殊的例子来进行原理说明:
i 为遍历后缀的索引,k为遍历前缀的索引
这里约定:i 指向的位置为黄色,k指向的位置为绿色

1.当该元素的前面的字符串具有公共前后缀时,当这个元素和前面字符串的公共前缀的下一位相等,那么就会形成在原来长度基础上+1的新的最长公共前后缀
当字符串遍历到b时,此时字符串为ababcabab
在这里插入图片描述

如上图,此时在对k = 3(绿色),i = 8(黄色)位置进行判断,此时完成上面最后蓝色位置的判断后,黄色b应该保存前面字符串的最长的公共前后缀3,然后此时i 指向黄色位置,k指向绿色位置,我们发现只要黄色和绿色位置的元素相等,他们就可以在前面的基础上形成一个新的最长公共前后缀,长度加1,为4,存储在如图橘色位置,如此 那么如果
s.charAt(i) == s.charAt(k)
且其前面的字符串有公共前后缀,则下个元素存储的Next数组中的值会在前面的值的基础上加1
Next[++i] = ++k;

2.当这个元素和前面字符串的公共前缀的下一位不相等时
我们接着上面的情况继续遍历下去,上面判断完i(黄),k(绿)色的字符后,i 增加到下图黄色位置,k增加到下图绿色位置,并把长度4(如图所示的蓝色公共前后缀)存储在其Next[ ]数组里,此时k = 4,i = 9 ,字符串为ababcababa
此时判断在 k = 4 和 i = 9 的位置的字符是否相等,不相等的话我们令 k = Next[ k ];
在这里插入图片描述
k = Next[k];
然后重复的判断,k = Next[ k ] 时 与 i = 9 时的字符是否相等
原理解释,因为上图的绿色与黄色是对应关系,在他们之前的字符串,上图蓝色是相同的,而我此时读取上图绿色位置的Next数组的值,就可以知道上图绿色前面的字符串有几位字符串长度的公共前后缀,在这个例子当中我们可以知道是两个,即下图的蓝色和紫色是相同的,因为绿色和黄色位置前面字符串是相同的,所以黄色前的字符串也有类似的性质
那么我们知道绿色前面的字符串的蓝色与紫色相等,而黄色前面的字符串和绿色前面的字符串相同,且其蓝色也等于紫色,那么 蓝1(第一部分蓝色) = 紫1(第一部分紫色),紫1 = 紫2(第二部分紫色),可知 蓝1 = 紫2,这样我们就直接知道了其除去上图蓝色外的最长的公共前后缀
在这里插入图片描述
在这个基础上我们只用完成下图 k = 2(绿) 和 i = 9(黄)的位置的字符判断,而减少判断其前面的蓝色部分的字符串部分,如果k = 2(绿) 和 i = 9(黄)的位置的字符不相等的话,重复上述操作,k = Next[ k ],得k = Next[ 2 ] = 0,然后重复上面的操作,最后把最长公共前后缀的长度存储在下图橘色位置
在这里插入图片描述

KMP算法怎么实现对匹配原理

我们仍然举例子来介绍这种匹配方式,如下有:
主串S:ababcabcacbab,模式串T:abcac
传统的BF解法是 在主串 ababcabcacbab 的每一个元素开始把子串匹配,直到匹配成功,这种匹配方式需要 把 主串的 指针 i 回溯
BF 代码

int BF(char S[],char T[])
{int i=0,j=0;while(S[i]!='\0'&&T[j]!='\0'){if(S[i]==T[j]){i++;j++;}else{i=i-j+1;j=0;}}if(T[j]=='\0') return (i-j);     //主串中存在该模式返回下标号 else return -1;     //主串中不存在该模式 
}

BF的流程
约定:i绿色指向主串,k黄色指向子串
第一次匹配(从 i = 0 开始)
在这里插入图片描述
匹配到 i = 2 和 j = 2 时发现不匹配,i 退回到 i = 1 ,从 i = 1 ,j = 0开始重新匹配
在这里插入图片描述
第三次匹配(从 i = 2 开始匹配)
发现 i = 6 和 j = 4 不匹配,蓝色是起始位置,下次从 i = 3,j = 0 开始匹配
在这里插入图片描述
上面就是BF算法的实现,我们发现每次匹配失败后,i都要回退,这样需要消耗大量时间,而如果我们可以读取子串该匹配元素的前面字符串的最长公共前缀(橘色),那么看图一,图中蓝色位置的字符串相等,而根据其公共前后缀相等,看图二,其中蓝色部分相等,那么我们的i就可以不回退,而令j = 该位置Next[ ]数组存储的元素,再进行判断,看图三,我们知道此时i 仍然等于 6,j = 1,此时进行遍历
而其前面字符串如果没有公共前缀,那么j = 0,就是从子串头开始遍历

图一:
在这里插入图片描述
图二:

在这里插入图片描述
图三:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.cpky.cn/p/11832.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

从头开发一个RISC-V的操作系统(二)RISC-V 指令集架构介绍

文章目录 前提ISA的基本介绍ISA是什么CISC vs RISCISA的宽度 RISC-V指令集RISC-V ISA的命名规范模块化的ISA通用寄存器Hart特权级别内存管理与保护异常和中断 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提…

磁盘管理与文件管理

文章目录 一、磁盘结构二、MBR与磁盘分区分区的优势与缺点分区的方式文件系统分区工具挂载与解挂载 一、磁盘结构 1.硬盘结构 硬盘分类&#xff1a; 1.机械硬盘&#xff1a;靠磁头转动找数据 慢 便宜 2.固态硬盘&#xff1a;靠芯片去找数据 快 贵 硬盘的数据结构&#xff1a;…

深入浅出 -- 系统架构之微服务中Nacos的部署

前面我们提到过&#xff0c;在微服务架构中&#xff0c;Nacos注册中心属于核心组件&#xff0c;通常我们会采用高性能独立服务器进行部署&#xff0c;下面我们一起来看看Nacos部署过程&#xff1a; 1、环境准备 因为Nacos是支持windows和Linux系统的&#xff0c;且服务器操作…

面试经典算法系列之双指针1 -- 合并两个有序数组

面试经典算法题1 – 合并两个有序数组 LeetCode.88 公众号&#xff1a;阿Q技术站 问题描述 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#…

游戏引擎架构01__引擎架构图

根据游戏引擎架构预设的引擎架构来构建运行时引擎架构 ​

WPS二次开发专题:如何获取应用签名SHA256值

作者持续关注WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 在申请WPS SDK授权版时候需要开发者提供应用包名和签…