KMP算法和BF算法
串(String)是由零个或多个任意字符组成的有限序列。如下图所示
目录
一、BF算法
二、KMP算法
一、前缀、后缀,最大公共前后缀
二、研究KMP与BF的区别 (主要是回溯)
三、next数组的讲解
j 位置的确定 (即next数组的详解)
当n=0时,为空串,与空格串不同。
一个串中任意个连续字符组成的子序列(包含空串)成为该串的子串。子串的概念类似于数学中的集合,有真子串,如下图所示
图来自于bilibili 王卓老师
具体初始化,创建等就不讲了,实际开发中不可默守陈规,关键在于学习两种算法
一、BF算法
此算法也称为简单匹配算法。实际上即是暴力穷举法,算法时间复杂度为O(n*m),为方便理解算法先看下面图解
若刚开始S[0]!=T[0],条件同样满足,因此只要S[s]!=T[t],回溯就成立。循环此步骤的条件为s,t均小于该串长度
当前面元素都满足条件S[s]==T[t],且进行到T[t]=="/0"时,代表匹配到了字符。此算法每次都要回溯因此时间复杂度为0(n*m)
代码实现如下(我以字符数组为例主要演示算法)
char S[7] = {"abcacbb"};char T[4] = {"cacb"};int s = 0;int t=0;while (s<sizeof(S)&&t< sizeof(T)) { //j范围在主串内,t范围在子串内if (S[s]==T[t]) {s++;t++; //值相等则比较下一元素}else {s = s - t + 1; //回溯子串主串t= 0;}if (T[t]=="/0") {return 1; //找到了}else {return 0; //还未到循环结束} }
二、KMP算法
KMP算法主要是在回溯方面进行了优化,使得不至于傻瓜式的回溯到头,关键点在于找到最大前后缀以减少无谓的过程。
由于网上的讲法有很多,我这面主要拆分为完整过程和next数组两部分讲解,我这面归纳总结为以下几个步骤:
KMP算法流程
①前缀,后缀,最大公共前后缀 概念
②研究KMP与BF的区别 (主要是回溯)
③next数组的讲解
一、前缀、后缀,最大公共前后缀
前后缀的概念直接看如下范例,解释起来不如看例子理解的快,这个地方无需考虑原理什么的,就是需要用到这三个概念。当做平常知识即可,
有一条字符串 为 “abcde”
前缀的集合:{a,ab,abc,abcd}
后缀的集合:{e,de,cde,bcde}
公共缀:就是该字符串 前缀后缀都包含的元素如下图所示
最大公共前后缀:如图中所示字符串“a b c a b d a”有 公共的前后缀为 a,b,ab三种情况,但最大的公共前后缀 就为ab.
失配位:即不相等时的位置。
那么最大公共前后缀有什么意义呢?接下来就进入正题。(本段思路来自天勤考研)
我们设定主串为 S[11]={"abcabbbabc"},子串为 T[5]={"abcabc"},i,j分别为数组下标
如下图所示,S[i] 与 T[j] 分别对应,直到 i=j=5时,即第六个元素时,二者不相等
当遇到不相等的情况是,BF的算法是将i回溯到 i-j+1 的位置,即之前比较的后一个位置
然而对于Kmp算法则不同,Kmp首先找到主串与子串相同的部分的最大公共前后缀,
其次将子串的前缀挪到与主串后缀的地方对齐。再进行S[i] 与 T[j] 的比较。
总结:步骤为 ①找到 失配位 ② 找到最大公共前后缀
二、研究KMP与BF的区别 (主要是回溯)
那么为什么可以直接跳过这些元素而直接比较呢,即为什么 i 可以不回溯,j回溯位置也不用从头开始呢。那么接下来就进行此步骤的剖析:
当遇到不相等元素时证明主串与子串在此元素之前一定都相等。那么先看相等这一部分,如图所示,若子串向右移动,不管移动多少次,子串头元素a只有遇到后缀a时才会相等。那么同理b也是,因此我们直接找到最大公共前后缀,进行对齐即可。这里的移动只是方便看图,实际上移动的 只有 j 。这里的最大公共前后缀是在失配位之前寻找,(即绿线之前)
由此步骤我们得出代码如下所示
图片来自bilibili 懒猫老师
BF算法在本文上面有提到,那么Kmp算法与其不同的地方主要在于判定条件,与回溯两处。此处判定条件 j=-1 会越界因此要加上条件,具体在后面会有讲到。j=next[j] 是 j 的回溯条件,即如何确定 S[i] 与 T[j] 不相等时,j 的回溯位置 !!!再此强调 i 是不用回溯的,敌动我不动即可。只有在匹配成功后才会和 j 一同跳到下一个位置。
三、next数组的讲解
那么以上所有步骤,就是Kmp算法的整体架构,接下来我们只需要考虑 next 数组了,同时这里也是最关键的地方 !!!
next数组的意义在于找到每次 S[i] 与 T[j] 不相等时,即失配位时,j 的回溯位置 , 这里我们先以手算法,讲解以便于理解后面的代码部分
事实上,在我们“移动”的过程中,根本不需要看主串,只需要看 子串(模式串)即可,直接将字串的最大前缀移动到最大后缀出即可如下图所示。此时我们再此强调,所谓的移动指示假想的,实际上是 j 的移动,如图 j 从原来失配位移动到了后缀之后。
j 位置的确定 (即next数组的详解)
一、手算图解法
此法仅仅为了方便观察对应代码来思考方便理解。(这里我们用新的一组子串来演示)
由上图可知,想要判断最大公共前后缀只需要子串即可,那么我们制作以下表格
这里的 T 代表了子串(模式串)后面数值为 子串内容,下标为串数组对应下标,next即为 j 要回溯的位置。
注意:这里next不同视频教程,有不同的版本,大概讲解一下,其实就是各个位右移的区别。
本文采用第二种方式 (这里不懂下面讲)
next数组的求法
第一步:数组初始化:
我们设定 j 为前缀 i 为后缀 。数组初始化 j 就等于0,对应的数组元素就等于 a,i为后缀所以 i=3,此时的 j 对应的 next值即为 -1.这里可以是0,也可以是-1,对应的上面的表,那么数组外部的 j=next [] =-1; 此时就对应之前的判断条件,为什么要加上j=-1。
第二步:求next [ j ]的值
求法是前n项 是否有公共前后缀,没有填0,有一个则填1,有两个填2,以此类推
当 j =-0时,数组初始化, next [1] = -1;
当 j =1 时,前面就只有a一个元素,所以 next [1] 应该为 0;
当 j =2 时,前面有a b 两个元素, 所以 next [2] 应该为 0;
当 j =3 时,前面有a b a 三个元素,所以 next [3] 应该为 1;(有公共前后缀 a)
后面以此类推,结果如下表
二、代码解法
将图表和代码结合看
总结:j 始终指向 a的位置,移动的为 i ,当遇到 j==-1 或 T [ i]=T [ j ]时才会+1,进行下一元素比较,如果相等其实是代表了最大公共前后缀有2位,若不相等,则证明只有一位最大公共前后缀,与此同时,j=next[ j ],也就是回退到值等于a的地方。
KMP算法和BF算法相关推荐
- 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)
图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...
- 操作系统之存储管理——FIFO算法和LRU算法
操作系统之进程调度--优先权法和轮转法(附上样例讲解) 操作系统之银行家算法-详解流程及案例数据 操作系统之多线程编程-读者优先/写者优先详解 操作系统之存储管理--FIFO算法和LRU算法 操作系统 ...
- Prim算法和Kruskal算法
Prim算法和Kruskal算法都能从连通图找出最小生成树.区别在于Prim算法是以某个顶点出发挨个找,而Kruskal是先排序边,每次选出最短距离的边再找. 一.Prim(普里姆算法)算法: ...
- 基于Huffman算法和LZ77算法的文件压缩的改进方向
基于Huffman算法和LZ77算法的文件压缩(八) 到这里已经简单实现基于Huffman算法和LZ77算法的文件压缩, GitHub源码:点我 根据基于Huffman算法和LZ77算法的文件压缩(七 ...
- 最短路径Dijkstra算法和Floyd算法整理、
转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最短路径-Dijkstra算法和Floyd算法 Dijks ...
- 用Spark学习FP Tree算法和PrefixSpan算法
在FP Tree算法原理总结和PrefixSpan算法原理总结中,我们对FP Tree和PrefixSpan这两种关联算法的原理做了总结,这里就从实践的角度介绍如何使用这两个算法.由于scikit-l ...
- 游戏中DDA算法和Bresenham算法的应用
在角色扮演或即时战略游戏中,经常会将角色以最佳的方式走到指定地点.游戏场景的地面情况复杂,而且场面大,若采用盲目式搜索,例如盲目穷举法,则几乎要遍历整个场景,效率非常低,造成角色反应速度过慢,实践证明 ...
- 【Java数据结构与算法】第二十章 Dijkstra算法和Floyd算法
第二十章 Dijkstra算法和Floyd算法 文章目录 第二十章 Dijkstra算法和Floyd算法 一.Dijkstra算法 1.介绍 2.代码实现 二.Floyd算法 1.介绍 2.代码实现 ...
- 【Java数据结构与算法】第十九章 贪心算法、Prim算法和Kruskal算法
第十九章 贪心算法.Prim算法和Kruskal算法 文章目录 第十九章 贪心算法.Prim算法和Kruskal算法 一.贪心算法 1.介绍 2.支付问题 二.Prim算法 1.最小生成树 2.介绍 ...
最新文章
- api.dll自己的理解
- jQuery的Accordion插件
- 你有一笔新订单 语音_上市即成爆款 哪吒V首日订单突破1200辆_搜狐汽车
- WPF 模仿QQ音乐首页歌单效果
- 修改ant design vue中的Icon图标颜色
- log4j自定义配置文件(SpringMVC项目)
- 携号转网将于明年在全国范围内实施
- 达梦工作笔记-达梦客户端,执行命令后要保存退出,才生效
- JAVA学习篇--Java类加载
- 如何用c语言编写工程文件夹,利用makefile实现c语言项目编译
- Spring 官方又孵化了个顶级项目,或将改变前后端API现状!
- 2017-07-12 周三 今日总结
- 5.1 Zend_Log_Writer
- 吾不是爱管闲事,实在是忍无可忍
- Linux-3.10-x86_64 内核配置选项简介
- Swiper.js实现无缝滚动
- Word文档被限制编辑了怎么办?
- python 笔记6:格式化时间缩写
- RGB-Infrared Cross-Modality Person Re-Identification---阅读
- 安全渗透测试-win7
热门文章
- Cas(05)——修改Cas的其它配置
- 安装包免费下载(持续更新ing…)
- 最新Laysns仿ZB响应式CMS轻主题 可做资源娱乐网
- kafka命令及启动
- 苹果应用商店AppStore审核中文指南(译本)
- android面试资料
- 本地 Services(服务)
- 设置文本框 input [type=text] 的长度不超过固定值,输入的字符不超过固定值
- java实现魔方_闲来无事,用java写了一个魔方小程序。附源码 | 学步园
- 【CEPH-初识篇】ceph详细介绍+“ 一 ” 篇解决ceph集群搭建, “ 三 ” 大(对象、块、文件)存储使用