KMP算法以及KMP算法的优化
KMP算法是基于串的模式匹配的一种比较时间复杂度较低的一种算法。
那么什么是模式匹配以及KMP算法究竟好在哪里呢?
串的模式匹配:子串的低位操作通常称为串的模式匹配,它求的是子串(模式串)在主串中的位置。
我们这里对串的两种模式匹配进行一下对比
第一种就是简单的模式匹配算法
简单的模式匹配算法是很容易理解也是大部分同学常用的算法,也可以说是暴力匹配算法
我们这里简单描述一下暴力匹配的算法思想
分别用技术指针i和j只是主串S和模式串(子串)T中当前正待比较的字符位置
算法思想为:从主串S的第一个字符起,与模式串T的第一个字符比较,若相等,则继续逐个比较后续字符,否则从主串的下一个字符起重新和模式串的字符比较,以此类推,直至模式串T中的每个字符在主串S中的一个连续字符序列相等,则称匹配成功,返回的函数值为与模式串T中第一个字符相等的字符在主串S中的序号,否则称匹配不成功,返回的函数值为0。
算法实现如下
int Index(String S,String T){int i=1,j=1;while(i<=S.length && j<=T.length){if(S.ch[i]==T.ch[j]){++i;++j;}else{i=i-j+2;j=1}}if(j>T.length){return i-T.length;}else{return 0;}
}
下图充分展示了模式串T=‘abcac’和主串S匹配的过程
这便是常用的暴力匹配算法,显然我们的主串指针i遇到不符合模式串T的情况时需要回溯这就大大降低了我们的匹配效率
设主串长度为m,模式串长度为n
由此可以的出暴力匹配算法最差的时间复杂度为O(mn)
那么有没有不需要主串i回溯的算法进行匹配呢
这时我们的KMP算法就该登场了
由D.E.Knuth,J.H.Morris和V.R.Pratt提出的算法,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的
刚刚我们便说了,KMP算法核心思想是利用匹配失败后的信息尽量减少模式串与主串的匹配次数
这时候我想引用刚刚我们暴力匹配上图的第三趟匹配,如图所示
由图可以知道我们的b和c是不能够匹配成功的那么既然不想让主串的指针i进行回溯
假设i只能往后移动那么此时的模式串abcac应该从哪里匹配呢
显然如果我们的j再回到1利用主串的b和模式串的a进行匹配是不合理的
因为此时的i=7指向的b
但是如果j=1指向模式串的a就忽略了主串中位序为6即i=6的a
而恰好可以看出模式串在主串的位置正是由i=6开始的
如果重新赋值j=1的话那么就不会匹配成功
所以此时的j应该是等于2再对i=7进行匹配
即i=7与j=2进行匹配
可以的出两个字符是相等的继而进行i++和j++直到j大于了模式串T的长度退出
便是匹配成功
可知KMP算法主串的指针不会回溯
而模式串的指针也是根据一定规则进行回溯的
这样就大大减少了不必要的匹配
那么针对不同的模式串我们以什么依据什么规则进行对j的回溯调整呢
这时候我们要想了解模式串回溯的规则就要引用到一个新的概念
字符串的前缀、后缀和部分匹配值
前缀指除了最后一个字符以外,字符串的所有头部子串
后缀指除了第一个字符以外,字符串的所有尾部子串
部分匹配值则为字符串的前缀和后缀最长相等的前后缀长度
下面我们以模式串‘abcac’在主串中的匹配为例进行说明
如何手动计算部分匹配值
如下图第一趟匹配
当我们匹配到a与c进行比较时
发现a与c并不同所以我们的模式串需要回溯,回溯的位置就要看我们模式串当前指向的位置c前面的部分子串
由此可见c前面的子串为ab并且是匹配的
要求ab的部分匹配值我们需要知道ab的前缀和后缀
根据我们刚才对前缀和后缀的定义
ab的前缀为{a},后缀为{b}
而部分匹配值为字符串的前缀和后缀最长相等的前后缀长度
而abc的前后缀并没有相等的子串所以匹配长度为0
在实际KMP算法中,为了使公式简洁,计算简单,如果串的位序是从1开始的,则我们计算的部分匹配值就要整体加1,如果串的位序是从0开始的,则next数组不需要整体加1
为了方便大家理解这里我做整体加1处理
刚刚我们说的ab的部分匹配值为0,我们进行加1处理,那么部分匹配值为1
此时的这个部分匹配值即为我们的j所要回溯到的位置即j=1,指向a的位置继续与主串中的i=3指向的位置进行比较
由此可见我们就省略了暴力匹配中的第二趟的匹配,来到了第三趟的匹配
我们继续来看第三趟的匹配
如图所示
此时我们的b和c又匹配不相符了那么我们的i继续保持不变,只回溯j,那么j应该回溯到哪里呢
就要看当前j指向的c前面的部分子串即‘abca’并且是与主串匹配的
我们继续来利用‘abca’的前缀和后缀来求部分匹配值
abca的前缀分别为:{a},{ab},{abc}
abca的后缀分别为:{a},{ca},{bca}
最长相等前后缀长度为1
因为我们的位序是从1开始的所以部分匹配值需要加1
所以这里的部分匹配值为2即为我们的j需要指向的位序就是模式串中的b
进而继续完成匹配
我们在这里总结一下关于‘abcac’求部分匹配值的表格用来帮助理解
串 | 前缀 | 后缀 | 最长相等前后缀长度 |
---|---|---|---|
‘a’ | ∅ | ∅ | 0 |
‘ab’ | a | b | 0 |
‘abc’ | a,ab | c,bc | 0 |
‘abca’ | a,ab,abc | a,ca,bca | 1 |
‘abcac’ | a,ab,abc,abca | c,ac,cac,bcac | 0 |
而我们算出的最长相等前后缀长度以及匹配值如何应用到程序中,就需要我们建立一个数组来存储最长相等前后缀长度或者是匹配值
我们暂且定义为数组next
那么如果按我这种理解,位序从1开始计算那么next[]={0,1,1,2,1}
也许有疑问为什么next[0]=0呢
因为当next[0]=0的时候说明我们的模式串第一个字符就与当前主串中的位置不匹配
用来提醒主串中的指针此时应该往后移动了
这里会在我们下面的程序中进行体现
int KMP(String S,String T,int next[]){int i=1,j=1;while(i<=S.length&&j<=T.length){if(j==0||S.ch[i]==T.ch[j]){++i;++j;}else{j=next[j];}if(j>T.length){return i-T.length;}else{return 0;}}
}
我们只是手动计算了next数组,其实KMP算法最主要的还是求解next数组,与求解next数组相比,KMP匹配算法相对要简单一些
它与简单的模式匹配很相似,不同之处仅在于当匹配过程失效时,指针i保持不变,指针j退回到next[j]的位置并重新进行比较,当指针j=0时,使指针i和j同时加1,即如果主串的第i个位置与模式串的第一个字符不等,则应从主串的第i+1个位置开始匹配。
这里写一份求解next数组的程序供大家参考,程序还是以模式串abcac为例进行求解next数组
虽然模式串只有5个字符但是还是程序比较绕的,需要多理解几遍。
void get_next(String T,int next[]){int i=1,j=0;next[1]=0;//直接将next[1]赋0,为了方便理解我们的next[0]不用 while(i<=T.length){if(j==0||T.ch[i]==T.ch[j]){//注意这里的i和j都是指向的模式串的字符进行对比 ++i;++j;next[i]=j;}else{j=next[j];}}
}
//可以针对abcac进行推几遍自己得出next数组
//当然这里的next数组是整体进行了加1处理的
//而我们在上文中的表格手动计算的并没有进行加1处理
所得到的next数组也为next[]={0,1,1,2,1}
然后根据next数组来进行KMP算法
由此可以看出KMP的时间复杂度为O(m+n)
但是一般情况下普通匹配实际执行时间近似为O(m+n)因此至今仍被采用
KMP算法在主串与子串有很多部分匹配时才显得比普通算法要快的多
其主要优点是主串不回溯。
KMP算法以及KMP算法的优化相关推荐
- 数据结构与算法(5)字符串(BF算法、KMP算法及KMP算法优化)
目录 一.BF算法(暴力算法) 二.KMP算法 三.KMP算法优化 一.BF算法(暴力算法) 一个一个往后匹配,匹配失败继续从母串下一个和头(子串的头)往后继续匹配. 虽然简单,但是需要较多的时间复杂 ...
- KMP 看毛片算法原理及其实现
kmp算法 前言: 如何匹配字符串??? 一. 暴力匹配字符串 1.1 暴力算法描述 1.2 暴力算法实现 二. KMP算法 匹配字符串 2.1 三个概念: 最长前缀; 最长后缀; 最长公共前后缀? ...
- BF算法及KMP算法的实现
目录 前言 一.BF算法 1.BF算法是什么 2.BF算法的实现 二.KMP算法 1.KMP算法是什么 2.next数组 3.代码实现 总结 前言 例如:随着我们对字符串的不断学习和深入了解,我们会面 ...
- 【C语言】算法学习·KMP算法
KMP算法(全称Knuth-Morris-Pratt字符串查找算法,由三位发明者的姓氏命名)是可以在文本串s中快速查找模式串p的一种算法. 要想知道KMP算法是如何减少字符串查找的时间复杂度的,我们不 ...
- 常用十大算法 非递归二分查找、分治法、动态规划、贪心算法、回溯算法(骑士周游为例)、KMP、最小生成树算法:Prim、Kruskal、最短路径算法:Dijkstra、Floyd。
十大算法 学完数据结构该学什么?当然是来巩固算法,下面介绍了十中比较常用的算法,希望能帮到大家. 包括:非递归二分查找.分治法.动态规划.贪心算法.回溯算法(骑士周游为例).KMP.最小生成树算法:P ...
- KMP(Knuth-Morris-Pratt) 字符串查找算法
1.背景 我接触到这个算法是在力扣的每日一题中(28. 实现 strStr()),这本来只是一个分类为简单的题目.但是却在官方题解中介绍了这个看似十分复杂的算法. 在官方题解中给出了详细的证明 ...
- 数据结构与算法之KMP算法
数据结构与算法之KMP算法 目录 KMP算法介绍 输入字符串str1,str2,返回字符串str2是否在str1中,在的话在第几位开始 1. KMP算法介绍 在CSDN上看到一篇写的很好的关于KMP的 ...
- 字符串处理 —— 单模式匹配 —— MP 算法与 KMP 算法
[概述] KMP 是在 MP 算法的基础上改进出来的,两者的核心思想与匹配过程相同,唯一不同的是在于 next 数组的求法,其目的是为了避免 MP 算法中明显失败的匹配. KMP 算法又称 Knuth ...
- 【数据结构】字符串 模式匹配算法的理解与实现 Brute Force算法(BF算法)与KMP算法 (C与C++分别实现)
#笔记整理 若不了解串的定义,可至: 串(string)的定义与表示 查看 串的模式匹配算法 求子串位置的定位函数 Index(S, P, pos) 求子串的定位操作通常称作串的模式匹配(其中子串P称 ...
- 《数据结构》实验报告四:串的模式匹配(BF算法、KMP算法)
一.实验目的 1.了解串的基本概念. 2.掌握串的模式匹配算法的实现 . 二.实验预习 说明以下概念 1.模式匹配: 串的模式匹配就是子串的定位运算. 设有两个字符串 S 和 T ,S为主串(正文串) ...
最新文章
- liunx 下 sendmail 反病毒和防垃圾邮件
- HTML5 蔡徐坤打篮球游戏 NMSL❤️❤️❤️
- UVA - 10168 Summation of Four Primes(哥德巴赫猜想)
- 简历javaweb项目描述怎么写_硬件工程师简历-项目经验怎么写【范文】
- [转] 撞车之后,不要傻里傻气的!
- 服务器密闭通道天窗维修,机柜及密闭通道技术规范
- 手机html己停用怎么办,iphone手机出现已停用请五分钟再试怎么办
- imagej得到灰度图数据_ImageJ的高级使用方法
- 笔记本电脑开机黑屏没反应怎么办
- 生信识图之 点图进阶-3(MA)
- [学习笔记]自适应辛普森(Simpson)积分
- Win11蓝屏代码IRQL NOT LESS OR EQUAL的处理方法
- 令人匪夷所思的 Magic 之魔数,你真的不会
- Removing-Camera-Shake-from-a-Single-Photograph图像去模糊读书笔记
- 语音控制小车运动APP(基于百度语音识别)
- Dell商用台式机、笔记本、服务器800电话
- 阿里云小程序云发布小程序跨平台开发框架,助力开发者一次开发,多端运行
- 反恐精英代码_游戏《反恐精英:全球攻势》《军团要塞2》源代码遭泄露
- 简单的意图分析模型(1)-数据处理
- 用Java计算圆周率的十亿位_新世界纪录:谷歌将圆周率计算到 31 万亿位