高效KMP字符匹配算法就这么简单
1、聊一聊
上一篇文章
"暴力"字符匹配算法的C语言实现
2、KMP算法介绍
1
KMP介绍
KMP是一种字符匹配算法,为啥叫KMP呢?因为是由D.E.Knuth,J.H.Morris和V.R.Pratt大佬提出来的。那一些小伙伴会问了怎么不叫"DJV算法"呢?因为老外都是名字在前,姓氏在后。
说实在的bug菌初次接触这个字符匹配算法的时候也是一脸懵逼,那时候也是找了很多的资料来理解、分析和推导等等,很多知识回头一看也就那么回事,想不明白那时候怎么会觉得那么复杂,或许这就是对未知事物的一种敬畏吧。
KMP算法说到底和暴力字符匹配功能上是一模一样的,就是查找匹配字符串在主字符串中的位置,如果只是应用完全可以不用理会它是怎么实现的,调用几个函数->传递几个参数->得到结果,然后记得他比暴力匹配高效一点就行了。好了,本文看来要收尾了。
2
还不能结束
其实我们学习理论知识并不是学习知识本身,而是要学习了以后能够获得一种解决问题的办法和思路。前面那篇文章为大家讲解了暴力匹配,也跟大家在文中留了一个问题,假如没有KMP算法我们是否有思路去更好的优化匹配效率呢?等思考完后再来看看KMP是如何处理该问题的。
KMP算法的核心 : 就是尽可能利用更多匹配过程中的信息来减少匹配串与主串的匹配次数从而提高匹配效率。
3、原理分析
1
几个实例分析
那肯定得把暴力匹配中需要优化的匹配过程拿过来,如下图所示:
匹配完abab以后匹配失败,按照暴力匹配会把模式串移动一个字节继续重头开始匹配,然而再次匹配必定不成功,得继续把模式串移动一个字节进行匹配。
前面我们讲到KMP算法利用已经进行过匹配过的信息进行优化匹配,如上图当进行第一次匹配以后,我们能够利用的信息有两个:1)模式串信息;2)已经匹配过的ababa信息,其他信息暂且还未知。
那么就简单点处理 : 如果已经匹配的字符串前两个字节与后面两个字节相等,那么就把模式串移动两个位,继续跟主串比较即可,因此从第三个字符开始进行接下来的匹配,无需重头开始匹配,最终匹配成功。
上图是最简单的情形,那么下面我们看另外一种情况:
按照之前的做法,如果已经匹配过的aaaa中前两个字节等于后两个字节,那么模式串应该移动两个字节,然而我们发现其只需要移动一个字节就能匹配成功了,看来仅仅用之前的策略还不够完善,还得补充其它策略才行。
2
总结规律获得算法
通过前面两个例子了解到,我们只需要通过一套统一的规律来指导第A个字符匹配不成功以后下一步该对第B个字符进行匹配即可。于是就形成一张映射表-next数组表(不用太纠结名字,主要的意思是下一步该如何动作所以叫next)。
这个next数组表是通过模式串获得的,首先给大家看看一个next表:
分析一下:
1)第一行就是模式串的各个字符,第二行是数组的标号,因为到时候会定义一个next[5]数组存放表格中的信息。
2)"前后缀等"表示的是包含比较失败字符串在内的字符前后缀相等的最大字符长度,而next值是前后缀向右填充并在前面补-1得到的数组值。结合下图和表格中的数据字符对应的next值是对应得上的。
3)那么我们可以检验一下,字符C比较失败以后从匹配字符[2]继续匹配,即模式串的第三个字符,跟之前的分析一致。
4)同样我们来看看aaaac模式串的next数组图:
5)同样加入我们在第3个a匹配失败和最后一个c匹配失败以后如何获得前后缀的呢?如下图所示,前面的例子没有重叠的前后缀出现,不过这里就出现了重叠的问题。
6)很明显如果字符C匹配失败,对应的需要从模式串[3]继续比较,即模式串的第四个字符继续比较,与前面的分析一致。
7)如果大家还看不懂前后缀,那bug菌再画一个图,你肯定就懂了:
4、KMP算法的C实现
1
实现代码
对于KMP算法的代码实现真的非常巧妙,而且特别的简短,如下是KMP算法的实现,KMP算法目前也存在比较多的改进版本,大家常用的还是如下实现:
1#include <stdio.h>2#include <stdlib.h>3#include <string.h>45#define NEXT_LEN 667char *Parent = "1234567891212123456789";8char *Chind = "121212"; 9int next[NEXT_LEN] = {0};1011/******************************************************12 * Fuction:KMP匹配算法查询函数 13 * Params : str1:主串;str2:模式串;next:next数组14 * (公众号 : 最后一个bug) 15 *****************************************************/16char* KMP(const char * str1, const char * str2,int * next) 17{18 int i = 0; 19 int j = 0;20 char * ret = (char*)str1;2122 while (i < strlen(str1) && j < (int)strlen(str2)) //主串结束、模式串成功 23 {24 //j = -1直接到next[0]处理或者字符匹配成功25 if ((j == -1)||(str1[i] == str2[j])) 26 {27 //下一个字符移动 28 i++; 29 j++;30 }31 else 32 {33 //如果匹配不成功通过j(模式串比较失败地址)找到next中下一次与主串比较的模式串地址 34 j = next[j]; 35 } 36 }3738 if (j == strlen(str2))//表示的是模式串全部匹配 39 {40 return (ret + i - j);41 }42 else 43 return(NULL);44}45/******************************************************46 * Fuction:KMP匹配算法next数组生成 47 * Params : str:模式串;next:next数组48 * (公众号 : 最后一个bug) 49 *****************************************************/50void getNext(const char * str, int * next)51{52 next[0] = -1;53 int i = 0, j = -1;5455 while (i < (strlen(str) - 1))56 {57 if ((j == -1 )||(str[i] == str[j]))//通过模式串自身对比获得next数组值 58 {59 ++i;60 ++j;61 next[i] = j;62 } 63 else64 { 65 j = next[j];66 }67 }68}6970/******************************************************71 * Fuction:暴力匹配算法 72 * Params :str1:主串;str2:模式串 73 * (公众号 : 最后一个bug) 74 *****************************************************/75char *strstr(const char *str1, const char *str2)76{77 char *cp = (char*)str1;78 char *s1, *s2;7980 if (!*str2)81 return((char *)str1);8283 while (*cp)84 {85 s1 = cp;86 s2 = (char *)str2;87 while (*s1 && *s2 && !(*s1 - *s2))88 {89 s1++, s2++;90 } 9192 if (!*s2)93 return(cp);9495 cp++;96 }97 return(NULL);98}99
100/******************************************************
101 * Fuction:对比主函数
102 * Author : (公众号 : 最后一个bug)
103 *****************************************************/
104int main(int argc, char *argv[]) {
105 int result = 0;
106 int i = 0;
107 //获得KMP的next数组
108 getNext(Chind,next);
109 for( i = 0; i < NEXT_LEN;i++)
110 {
111 printf("Next[%d] = %d\n",i,next[i]);
112 }
113 //进行KMP匹配
114 result = KMP(Parent,Chind,next)- Parent;
115 printf("KMP result = %d\n",result);
116 //进行暴力匹配
117 result = strstr(Parent,Chind) - Parent;
118 printf("strstr result = %d\n",result);
119
120 return 0;
121}
运行结果如下:
分析一下:
大家仔细阅读了会发现,其实求next数组和KMP匹配算法是非常的相似,KMP算法的关键就是求next数组,一旦匹配不成功就会去next数组中找下一个模式串的匹配点,再次拿着该匹配点与匹配失败主串进行比较.
所以KMP算法的优越之处在于不再需要重头开始比较,其主串的比较指针是不会倒退的。至于两个算法的时间上的复杂度KMP<strstr,这一部分的推导bug菌就不深入分析了,大家有时间可以查阅一下相关资料进行进一步阅读和学习,具体的应用bug菌在前面的暴力匹配有跟大家讲解过,这里就不在赘述了。
5、最后小结
KMP算法就跟大家介绍到这里了,希望大家能有新的收获,算法的话大家也不需要死记硬背,基本到处都能够找到,了解原理并且能够获得这种处理问题的思维就达到学习的目的了。
推荐阅读:
专辑|Linux文章汇总
专辑|程序人生
专辑|C语言
我的知识小密圈
关注公众号,后台回复「1024」获取学习资料网盘链接。
欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~
高效KMP字符匹配算法就这么简单相关推荐
- 动态规划-KMP字符匹配算法
动态规划之KMP字符匹配算法 把KMP看成输入不只有0/1的Moore型(数字逻辑)(直接ASCII:266或是大小写:52) ** 看成动归:状态,和选择 构建状态转移图 分成状态推进,与重启 要去 ...
- 动态规划之KMP字符匹配算法
动态规划之KMP字符匹配算法 文章目录 动态规划之KMP字符匹配算法 一.问题引入 二. KMP 算法概述 三. 状态机概述 四. 构建状态转移图 五. 代码实现 六. 最后总结 本文的KMP算法是通 ...
- 暴力字符匹配算法的C语言实现
1.聊一聊 今天跟大家分享的这个曲子一般是在天气预报和英语试听中出现,不信你听一听绝对有种似曾相识感觉. 本篇文章主要是为讲解高效字符匹配算法的一则预告文,跟大家讲讲暴力字符匹配算法以及匹配算法在通信 ...
- 数据结构与算法分析(十六)--- 如何设计更高效的字符串匹配算法?(BF + RK + KMP + BMH)
文章目录 一.Brute Force 匹配算法 二.Rabin–Karp 匹配算法 三.Knuth–Morris–Pratt 匹配算法 四.Boyer-Moore-Horspool 匹配算法 五.字符 ...
- 也许,你可以像我这样来理解KMP模式匹配算法
本文已在本人微信公众号"码农小阿飞"上发布,打开微信搜索"码农小阿飞",或者扫描文章结尾的二维码,进入公众号并关注,就可以在第一时间收到我的推文! 前言 不管是 ...
- KMP字符串匹配算法理解(转)
一.引言 主串(被扫描的串):S='s0s1...sn-1',i 为主串下标指针,指示每回合匹配过程中主串的当前被比较字符: 模式串(需要在主串中寻找的串):P='p0p1...pm-1',j 为模式 ...
- 串--串的定义,顺序、链式存储结构,BF、KMP模式匹配算法(C语言描述)
此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢! 一.串(String)的定义: 串(String):由零个或多个字符组成的 ...
- 使用Java实现高效的字符串匹配算法
摘要:字符串匹配是计算机领域中的一个重要问题,有着广泛的应用场景.在本篇博客文章中,我们将介绍几种高效的字符串匹配算法,并给出使用Java语言实现的代码示例,希望能对读者理解和应用这些算法有所帮助. ...
- 实现KMP字符串匹配
KMP 字符串匹配算法可以实现高效的匹配. 假设长字符串为t,短字符串为p.为了进行 KMP 匹配,首先需要计算字符串p的next数组,后面实现了计算该数组的函数void KmpGenNext(cha ...
最新文章
- springmvc整合swagger 与 常用注解说明
- SAP电商云CCV2 Restful API enablement
- 使用 WebBrowser 操作 js
- JavaScript,等比例缩放图片的函数,很好用。
- Mysql的select in会自动过滤重复的数据
- 2021高考成绩查询甘肃天水,2021年天水高考成绩排名及成绩公布时间什么时候出来...
- 私有服务器虚拟化软件市场排名,三大服务器虚拟化软件比拼 谁是最佳?
- OC5022B内置MOS的开关降压型恒流驱动芯片
- 免费无广告的统计工具TalKingData
- 《认知觉醒》 读书笔记
- frp服务端(frps) 安装及使用
- 浮窗---创建Activity浮窗(可拖动)
- 三子棋?玩我自己的!
- RN仿微信通讯录列表
- OSI七层模型的功能以及设备
- uniapp小程序运行正常,app运行报cid unmatched at view.umd.min.js
- 爬取今日头条收藏夹文章列表信息
- BNUZ-ACM 2019国庆新生欢乐赛 E.如风般奔跑(题目详解+代码)
- 黑马程序员_网络电视用中国电信路由器不能联网的问题
- python实现寻迹功能