高效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」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TQ210 —— NandFlash

TQ210 —— nandflashTQ210 开发板板载一片 1Gbyte 的 NAND FLASH——K9K8G08U0B&#xff0c;通过查询K9K8G08U0B 芯片手册可以得到如下信息&#xff1a;&#xff08;理论知识不再介绍&#xff09;K9K8G08U0B : (1G 32M) x 8bit 总大小Data Register : (2K 64) x 8bit 数据寄…

涂鸦赞助的500个开发套件,先到先得

来源 | 涂鸦跟涂鸦申请的福利&#xff0c;给喜欢开发的同学发放开发套件&#xff0c;只要是不是专门白嫖党&#xff0c;数量可以不做限制。去年疫情突发&#xff0c;封城、封小区以及人员隔离&#xff0c;让原本享受千般宠爱的主子们变成了靠吃猫砂、塑料袋度日的小可怜&#x…

Linux-C 编程 / 网络 / 超迷你的 web server

一、为生活寻找固定的支撑点1. 什么是生活的支撑点&#xff1f;让自己感到些许痛苦&#xff0c;但却会带来实实在在的充实感和成就感的事情。举个栗子&#xff0c;我的支点是运动、看书、研究技术。2. 固定的支撑点很重要&#xff1a;三个固定的要素&#xff1a;时间 / 空间 / …

20165326 java第四周学习笔记

第四周学习笔记 ch5 子类和父类子类只能有一个父类使用关键字extendsyclass 子类 extends 父类系统默认的祖先类Object&#xff08;java.lang包中&#xff09;继承&#xff1a;子类继承父类的方法可以直接作为实例方法调用&#xff0c;继承的成员变量和方法的访问权限不变&…

用VSTS进行网站压力测试

VSTS提供了一个丰富、强大的工具平台&#xff0c;融合了软件开发领域的各个角色&#xff0c;涵盖软件开发生命周期的各个阶段&#xff0c;包括设计&#xff0c;开发&#xff0c;测试&#xff0c;管理&#xff0c;而这一整套构件的融会贯通&#xff0c;让它可以有效地改善软件开…

第四周选做作业

相关知识点的总结 通过命令行引入参数递归循环课上内容的补做&#xff0c;结果截图 未完成内容:递归与循环 补做教材第二章&#xff0c;第三章编程题目 参考资料 2016-2017-2 《Java 程序设计》课堂实践项目编写一个Java应用程序,输出全部的希腊字母。_百度知道[]转载于:https:…

数据结构和算法,也就那么回事儿

金三银四来了&#xff0c;各大厂动静不小&#xff0c;都在储备人才&#xff0c;绝对是程序员面试的黄金时间了&#xff0c;不少同学也在后台反馈面试中遇到的一些问题&#xff0c;所以今天想跟大家说说算法。说起算法&#xff0c;那大厂面试是绝对必考的&#xff0c;可以说是一…

SecureCRT护眼设置

SecureCRT护眼设置Option —— Global Options —— Terminal —— Appearance —— ANSI COlor从左至右&#xff0c;从上至下值分别为&#xff08;RGB元组&#xff09;&#xff1a; 1&#xff1a;(0,43,53) (128,128,0) (0,160,0) (160,160,0) (255,128,128) (…

北美暴风雨,Linux5.12被延迟6天发布

上个月中旬&#xff0c;因为罕见的严寒天气和暴风雪&#xff0c;美国得州和俄勒冈州波特兰遭遇了持续多天的停电。Linux 创始人 Linus Torvalds 就生活在波特兰&#xff0c;多年来他一直在家中远程工作&#xff0c;并负责新内核的最终发布。之前我们报道过&#xff0c;内核社区…

C#复习笔记(3)--C#2:解决C#1的问题(可空值类型)

可空值类型 C#2推出可空类型来表示可以为null的值类型。这是一个呼声很高的需求&#xff0c;因为在常用的数据库中都是允许某些值类型可为空的。那么为什么值类型就不能为空呢&#xff1f;内存中用一个全0的值来表示null&#xff0c;但是全0的地址说明了这个内存空间是被清除了…

ISA之三种客户端访问

我们已经懂得怎么搭建ISA2006.我们今天来利用ISA访问外网&#xff01;顺便说声如果你IP&#xff0c;网关&#xff0c;DNS什么都没问题。那么你可以看看你的NAT处理&#xff01;下面我们看看我们的试验拓扑&#xff01;我们在ISA的服务器上做访问规则&#xff01;来允许我们可以…

面试官不讲武德,居然让我讲讲蠕虫和金丝雀!

1. 蠕虫病毒简介2. 缓冲区溢出3. 缓冲区溢出举例4. 缓冲区溢出的危害5. 内存在计算机中的排布方式6. 计算机中越界访问的后果7. 避免缓冲区溢出的三种方法7.1 栈随机化7.2 检测栈是否被破坏7.3 限制可执行代码区域8. 总结蠕虫病毒是一种常见的利用Unix系统中的缺点来进行攻击的…

asp.net core 拦击器制作的权限管理系统DEMO

效果图 没有登陆不会执行请求日期的方法&#xff0c;不管是否登陆都不允许访问请求时间方法 验证不通过是会进行转发到Home/error方法中&#xff0c; 代码附上&#xff1a; [Route("[controller]/[action]")]public class HomeController : BaseController{/// <s…

工程师姓什么很重要!别再叫我“X工”!!!

工程师之间都是这么互相打招呼的——“高工&#xff0c;你设计图通过了么&#xff1f;”“李工&#xff0c;工程画完了吗&#xff1f;”“王工&#xff0c;你真是越来越漂亮了&#xff01;”"张工&#xff0c;你的DFM整完了吗"“周公&#xff0c;Schedule 该更新了”…

说一下NFC,手机有NFC功能却不能模拟门禁卡?

img1、NFC是什么&#xff1f;NFC&#xff08;Near Field Communication&#xff09; 技术由Philips、Nokia和Sony主推的一种近距离无线通信技术&#xff08;NFCIP-1&#xff09;&#xff0c;是一种短距离非接触式的通信方式&#xff0c;通常有效通讯距离为4厘米以内。工作频率为…

小程序员的大梦想 与盖茨像哥们儿

小程序员的大梦想 与盖茨像哥们儿以10亿的天价转会新华都&#xff0c;让唐骏有机会开创其职业经理人生涯的新局面&#xff0c;但在此之前&#xff0c;他的成功已得到证明面前的唐骏温和得令人吃惊。仿佛“前微软中国区总裁”、“打工皇帝”、“10亿转会身价”这样的光环带给他的…

动态规划详解

个人见解&#xff1a;1.动态规划实现了把问题拆分成多个子问题&#xff0c;然后求解&#xff0c;子问题有解后&#xff0c;问题自然迎刃而解&#xff1b;2.动态规划实现了子问题的状态的迁移&#xff0c;保存每个状态值&#xff0c;递推出答案&#xff0c;但不记录每种状态的求…

我的奶奶

突然想起我奶奶&#xff0c;是突然发现&#xff0c;距离奶奶去世已经有快十年&#xff0c;这十年时间&#xff0c;我也再也没有见过奶奶。奶奶刚去世的前几年&#xff0c;有时候会梦到奶奶跟我说话&#xff0c;她总是会很耐心的告诉我很多道理。奶奶从小到大都没有指责过我&…

WPF应用程序内存泄漏的一些原因

原文&#xff1a;Finding Memory Leaks in WPF-based applications There are numbers of blogs that folks wrote about memory leaks in Microsoft .Net Framework managed code and unmanaged code based applications. In this blog I wanted to: Show coding practices th…

[转]Eclipse RCP应用系统开发方法与实战2-- 定制应用程序窗口属性

5.1.4 定制应用程序窗口属性 向导生成的应用程序主界面并不能满足要求&#xff0c;存在很多问题&#xff0c;例如&#xff0c;主界面运行时没有自动居中&#xff0c;主界面大小没有固 定&#xff0c;主窗口标题栏文字应该是“高校经费测算系统”。再看看图5-3、图5-4的标题栏有…