数据结构-4.6.KMP算法(旧版下)-朴素模式匹配算法的优化

一.绪论:

当主串字符和模式串字符不匹配时会执行j=next[j]来改变模式串的指针,但主串的指针不变。


二.求模式串的next数组:

1.例一:

如模式串abcabd,当第六个字符d匹配失败时,此时主串中前五个字符abcab都与模式串匹配成功,因此需要后移模式串:

模式串后移一个位置,显然主串中第二个字符b与模式串中第一个字符a不匹配,继续后移:

模式串再后移一个位置,显然主串中第三个字符c与模式串中第一个字符a不匹配,继续后移:

模式串再再后移一个位置,显然主串中第四个字符a与模式串中第一个字符a匹配,无需后移:

因此当模式串指针指向的第六个字符匹配失败时要让模式串指针从6回溯到3即next[6]为3:

2.例二:

如模式串abababcdef,当在第七个字符c匹配失败时,此时模式串中的前六个字符都能与主串匹配成功,最终需要模式串向右移动:

模式串后移一个位置,显然主串第二个字符b与模式串第一个字符a匹配失败,因此继续后移模式串:

模式串再后移一个位置,此时主串第三个字符a与模式串第一个字符a匹配成功,此时模式串无需后移:

因此当模式串指针指向的第七个字符匹配失败时要让模式串指针从7回溯到5即next[7]为5。
如果把模式串继续后移两个位置,此时模式串指针指向模式串的第三个字符a,而且模式串的第一个字符a和第二个字符b都可以匹配成功:

假设主串中字符如下图所示,此时主串指针i指向的字符和模式串第七个字符c不匹配,而且继续往后匹配,主串的c和模式串的第五个字符a匹配失败,如果按照刚才的next[7]为5,配对如下:

因此结论如下:如果有更长的一段能够匹配的上的话,优先考虑更长的这种情况

3.例三:

如模式串为aaaabcd,在第五个字符b和主串匹配失败,可知前四个字符aaaa都可以与主串匹配成功,此时需要把模式串后移:

模式串后移一个位置后,模式串前三个字符aaa都可以与主串匹配成功,此时应该让模式串里的指针回溯到4,判断是否能与主串匹配成功即next[5]为4:

显然,如果模式串再往后移一个位置,模式串和主串开头也能够匹配上,但一定能匹配成功的字符只有模式串中的前两个字符aa,主串中第五个字符还不确定是否为a,不往后移之前一定能匹配成功的字符有三个aaa,因此暂时不移动模式串较好;

4.例四:

如模式串为abcdefg,在第五个字符e时和主串匹配失败,因此模式串前4个字符abcd可以匹配成功,因此模式串需要后移:

模式串后移一个位置后,显然模式串第一个字符a与主串第二个字符b就不匹配,因此继续后移:

模式串后移一个位置后,显然模式串第一个字符a与主串第三个字符c就不匹配,因此继续后移:

模式串后移一个位置后,显然模式串第一个字符a与主串第四个字符d就不匹配,因此继续后移:

模式串后移一个位置后,此时模式串第一个字符a可能与主串第五个字符匹配成功,因此需要模式串指针从5回溯到1即next[5]为1,这个例子的特殊之处在于前几个例子中总是能找到主串已经确定的一些字符和模式串开始的字符能够匹配成功,但这个例子中主串中确定的字符中,无论后移几次模式串,都无法与主串匹配成功,所以之后需要判断主串中未知的字符能否与模式串中的字符匹配成功:

5.例五:

如模式串为abcabd,当检查到模式串中第一个字符a就发现与主串第一个字符匹配失败,此时模式串无需移动,需要检查主串的第二个字符能否与模式串中第一个字符a匹配成功:

如下图所示,此时可以先把模式串里的指针设为0:

然后主串指针和模式串指针同时加一,最终next[1]为0:

6.代码:

7.总结:

a.对于串的前缀,如模式串中第一个字符到第六个字符ababab,以ab为一段,第一个字符到第四个字符abab就是ababab的一个前缀,不包含最后一段;

b.对于串的后缀,如主串中的第一个字符到第六个字符ababab,以ab为一段,第三个字符到第六个字符abab就是ababab的一个后缀,不包含第一段;

c.注:前缀和后缀中前缀和后缀不一定只有一个字符组成,如ababab中a,ab,aba等都可以是它的前缀,后缀同理;

d.对于next[j]=S的长度等于前/后缀对的上的最大长度加一,如模式串中的S串(ababab),前缀最大就是abab,长度为4,所以S的最大长度为4+1等于5;

再比如上述图中右边的例子:模式串指针j在第五个字符时匹配失败,那么此时就要看前面的j-1个字符,由这些字符组成的字符串S(S为abcd),此时要找主串的后缀和模式串的前缀对的上的串的最大长度,先主串中从abcd后面开始取后缀d,再在模式串从abcd前面开始取前缀a,发现对不上,之后,主串中从abcd后面开始取后缀cd,模式串从abcd前面开始取前缀ab,发现还是对不上,之后,主串中从abcd后面开始取后缀bcd,模式串从abcd前面开始取前缀abc,发现还是对不上,现在主串和模式串就都不能再取了,因为主串取后缀,取后缀不能包含第一个字符,主串一共4个字符,从后面已经取了3个字符,第一个字符不能再取,模式串取前缀,取前缀不能包含最后一个字符,模式串一共4个字符,从前面已经取了3个字符,最后一个字符不能再取,所以S串最大长度为0,加一为1,因此next[5]为1:

比较特殊的情况,就是例五:


二.练习:求模式串的next数组

1.题目一:

思路:首先可以确定j为1时有next[j]为0;(其实next[1]必定为0,next[2]必定为1)

如果当 j为2即模式串指针指向的模式串第二个字符b时发现匹配失败,按照规则,第二个字符之前即第一个字符a组成的串S(只有一个a,a是第一个字符,也是最后一个字符),显然S串的前缀是空串(长度为0),因为前缀不包含最后一个字符,后缀也是一个空串(长度为0),因为主串中只有a与 模式串匹配成功,后缀不包含第一个字符,所以S串最大长度为0,加一为1,因此next[2]为1;

如果当j为3即模式串指针指向的模式串第三个字符a时发现匹配失败,说明第三个字符之前即ab都与主串匹配成功即串S为ab,串S的一个字符记为ab,显然串S没有前缀和后缀(串S取一个字符时S串最大长度也为0,当S串前缀为a时,就不能要b,对应的主串后缀就是b,就不能要a,显然前缀和后缀匹配失败,S串最大长度也为0),所以S串最大长度为0,加一为1,因此next[3]为1;

如果当j为4即模式串指针指向的模式串第四个字符b时发现匹配失败,说明第四个字符之前即aba都与主串匹配成功即串S为aba,此时前缀取一个字符a,后缀取一个字符a,只剩下b,所以S串最大长度为1,加一为2,因此next[4]为2;(其实前缀可以依次是a,ab,对应的后缀依次是a,ba,将前缀和后缀分别看成两个集合,这两个集合的交集只有一个a,所以S串最大长度为1,前缀和后缀也可以取aba,此时没有前缀和后缀,但这样的话S串最大长度为0,加一为1,因此next[4]为1,显然比next[4]为2低效,不可取,其他取前缀和后缀的原理同理,而且取前/后缀要考虑匹配最多的取);

如果当j为5即模式串指针指向的模式串第五个字符a时发现匹配失败,说明第五个字符之前即abab都与主串匹配成功即串S为abab,取前缀为ab,后缀也取ab(此时主串中有abab),显然前缀和后缀匹配成功,模式串中取了ab,长度为2,因此S串最大长度为2,加一为3,因此next[5]为3;

如果当j为6即模式串指针指向的模式串第六个字符a时发现匹配失败,说明第六个字符之前即ababa都与主串匹配成功即串S为ababa,如果前缀取aba,后缀也取aba(此时主串中有ababa,取的是第一个字符到第三个字符的aba,因为可以与模式串匹配上的字符多,不是第三个字符到第五个字符的aba),前缀和后缀可以匹配成功,模式串中取了aba,长度为3,因此S串最大长度为3,加一为4,因此next[6]为4;

2.题目二:

思路:首先可以确定j为1时有next[j]为0;可以确定j为2时有next[j]为1;

如果当j为3即模式串指针指向的模式串第三个字符a时发现匹配失败,说明第三个字符之前即aa都与主串匹配成功即串S为aa,显然前/后缀能匹配的上的最长的长度为1(取a,如果取aa的话就没有前缀和后缀了,此时前缀和后缀的长度为0),因此S串最大长度为1,加一为2,因此next[3]为2;

如果当j为4即模式串指针指向的模式串第四个字符a时发现匹配失败,说明第四个字符之前即aaa都与主串匹配成功即串S为aaa,显然前/后缀能匹配的上的最长的长度为2(取aa,如果取aaa的话前/后缀的长度为0,取a的话主串与模式串能匹配的字符最少,只有一个,不可取),因此S串最大长度为2,加一为3,因此next[4]为3;

如果当j为5即模式串指针指向的模式串第五个字符b时发现匹配失败,说明第五个字符之前即aaaa都与主串匹配成功即串S为aaaa,显然前/后缀能匹配的上的最长的长度为3(取aaa,前缀和后缀都能匹配上),因此S串最大长度为3,加一为4,因此next[5]为4。


三.求模式串的next数组的代码:

1.演示:

a.第二条求next[j]的语句中:等号左边的是模式串里的字符(即前缀),注意下标k-1,等号右边的是主串里的字符(即后缀),注意下标j-1,他们之间能够匹配的上的(相等)最大长度;k为模式串的指针,j为主串的指针;注意最长相等前后缀长度加一

b.第三条求next[j]的语句中:其他情况一般指前缀和后缀匹配失败时;

2.代码:

时间复杂度分析:从Index_KMP函数(Index_KMP函数中i为主串指针,i初始值为1,j为模式串指针,j初始值为1)开始,之后到get_next函数,然后到get_next函数里的while循环,其中i初始值为1,j初始值为0,T就是模式串,T.length就是模式串的长度,get_next函数里的while循环要循环1到T.length-1次,设T.length-1为m,那么get_next函数的while循环的时间复杂度为O(m),所以get_next函数的时间复杂度为O(m),执行完get_next函数,到了Index_KMP函数里的while循环,不需要考虑j,因为j是模式串的指针,主串的长度一般远大于模式串的长度,因此i的变化远大于j的变化,所以j就可以忽略不计了,主串指针i永远不回溯,最多不会超过主串的最后一个位置,所以while循环执行1到S.length次(S代表主串,S.length代表主串的长度),设主串的长度为n,那么Index_KMP函数里的while循环的时间复杂度为O(n),所以Index_KMP函数的时间复杂度为O(n),由此可得,Index_KMP函数的平均时间复杂度为O( (n+m)/2 ),等价于O(n+m)。


四.总结:

1.KMP算法让主串指针不回溯,只让模式串指针回溯;

2.朴素算法是主串指针和模式串指针都需要回溯;


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

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

相关文章

连锁店线下线上一体化收银系统源码

近年来线下线上一体化已经成为很多连锁门店追求的方向。其中,线下门店能够赋予品牌发展的价值依然不可小觑。在线下门店中,收银系统可以说是运营管理的关键工具,好的收银系统能够为品牌门店赋能。对于连锁品牌而言,对收银系统的要…

软媒市场新蓝海:软文媒体自助发布与自助发稿的崛起

在信息时代的浪潮中,软媒市场以其独特的魅力和无限的潜力,成为了企业营销的新宠。随着互联网的飞速发展,软文媒体自助发布平台应运而生,为企业提供了更加高效、便捷的营销方式。而自助发稿功能的加入,更是让软媒市场的蓝海变得更加广阔。 软媒市场的独特价值 软媒市场之所以能…

Android Studio Koala中Kotlin引入序列化Parcelable

找了一堆资料没有新构建序列化的方法,踩坑经历如下: 前提是使用Kotlin创建的项目 之前的build.gradle版本写法如下: 但是新版Android Studio Koala使用序列化模式发生了改变,如下: 测试成功如下: 发出来…

【万字长文】Word2Vec计算详解(三)分层Softmax与负采样

【万字长文】Word2Vec计算详解(三)分层Softmax与负采样 写在前面 第三部分介绍Word2Vec模型的两种优化方案。 【万字长文】Word2Vec计算详解(一)CBOW模型 markdown行 9000 【万字长文】Word2Vec计算详解(二&#xff0…

PyCharm+ssh跳板机+服务器

PyCharmssh跳板机服务器 文章目录 PyCharmssh跳板机服务器准备工作登录服务器查看CUDA查看conda创建虚拟环境 前言配置ssh免密登录设置ssh隧道配置pycharm测试第一种第二种 传输数据 准备工作 登录服务器 直接ssh连接就行,在终端(命令行)直接输入下面命令: 跳板机&#xff1…

windows系统更新升级node指定版本【避坑篇!!!亲测有效】(附带各版本node下载链接)一定看到最后!不用删旧版!

Node.js 是一个开源、跨平台的 JavaScript 运行时环境,广泛应用于服务器端和网络应用的开发。随着 Node.js 版本的不断更新,我们可能需要升级到特定版本以满足项目需求或修复安全漏洞。又或者是学习开发另外一个新项目,新项目对Node版本要求更…

数学建模算法与应用 第12章 现代优化算法

目录 12.1 粒子群优化算法 Matlab代码示例:粒子群优化算法求解函数最小值 12.2 遗传算法 Matlab代码示例:遗传算法求解函数最小值 12.3 蚁群算法 Matlab代码示例:蚁群算法求解旅行商问题 12.4 Matlab 遗传算法工具 使用遗传算法工具箱…

基于Python+sqlite3实现(Web)图书管理系统

项目名称:LibraryManagementSystem 一、系统目标 使用了Python作为语言,以django为后台,sqlite3作为数据库,UI基于bootstrap的图书管理系统,模拟图书管理的真实场景,考虑客观需求,界面简洁、操作方便&…

Android Studio实现安卓图书管理系统

获取源码请点击文章末尾QQ名片联系,源码不免费,尊重创作,尊重劳动 171安卓小说 1.开发环境 android stuido3.6 jak1.8 2.功能介绍 安卓端: 1.注册登录 2.图书列表 3.图书借阅 4.借阅列表 3.系统截图

Go编译为可执行文件

在window下打包成其他系统可运行的文件 1.在window下打包成window下可执行文件 在项目main.go同级目录下,逐条执行以下命令 set CGO_ENABLED0 set GOOSwindows set GOARCHamd64 go build -o main-windows.exe main.go 2.在window下打包成linux 在项目main.go同级目…

appium中的uiautomatorviewer显示的界面为横屏解决方法

uiautomatorviewer显示的界面为横屏解决方法 解决方法: 修改模拟器的分辨率,比如540:900就可解决了

鸿蒙NEXT开发-面试题库(最新)

注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下 如果大家觉得博主文章写的好的话,可以点下关注,博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

智能化叉车作业安全高效监控管理系统方案

在物流作业中,智能叉车管理系统的引入,不仅极大地提升了作业效率,还显著增强了作业安全性,为物流行业的现代化转型注入了强劲动力。 1、产品简介 2023A智能叉车管理系统是用于工业车辆安全监控管理的车载终端,具有快…

【数据结构与算法】线性表顺序存储结构

文章目录 一.顺序表的存储结构定义1.1定义1.2 图示1.3结构代码*C语言的内存动态分配 二.顺序表基本运算*参数传递2.1建立2.2初始化(InitList(&L))2.3销毁(DestroyList(&L))2.4判断线性表是否为空表(ListEmpty(L))2.5求线性表的长度(ListLength(L))2.6输出线性表(DispLi…

根据请求错误的状态码判断代理配置问题

SafeLine,中文名 “雷池”,是一款简单好用, 效果突出的 Web 应用防火墙(WAF),可以保护 Web 服务不受黑客攻击。 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、XSS、 代码注入、命…

如何高效撰写和发表SCI论文

第一章、论文写作准备即为最关键 1、科技论文写作前期的重要性及其分类 2、AI工具如何助力学术论文 3、研究主题确定及提高创新性 兴趣与背景:选择一个您感兴趣且有背景知识的研究领域。 创新性:选题和研究设计阶段如何提高学术创新性的方法。 研究缺…

yolov5-7.0模型DNN加载函数及参数详解(重要)

yolov5-7.0模型DNN加载函数及参数详解(重要) 引言yolov5(v7.0)1,yolov5.h(加载对应模型里面的相关参数要更改)2,main主程序(1)加载网络(2)检测推理&#xff0…

QD1-P2 HTML 编辑器:HBuilderX

本节学习: HTML课程内容介绍HBuilderX编辑器的使用 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p2 HTML 内容 基础语法 标签整体架构DOCTYPE 常用标签 标题和水平线段落和换行列表div 和 span格式化标签图片超链接标签表格表单字符实体 编辑器 HBuilder…

解决pyinstaller 打包 ddddocr 库方法

前言 ddddocr 库 在打包成 exe 文件后一直有各种各样的问题。无法运行。 总是提示缺少 onnxruntime_providers_shared.dll 等问题。例如下图: 所以这里总结一下打包解决方法。 方法 1、 第一步,先使用命令打包一次 pyinstaller -F demo.py -p D:\Python38\Lib\site-pac…

登录注册静态网页实现(HTML,CSS)

实现效果图 实现效果 使用HTML编写页面结构,CSS美化界面,点击注册,跳转到注册界面,均为静态网页,是课上的一个小作业~ 使用正则表达式对输入进行验证,包括邮箱格式验证,用户名格式验证。 正则…