字符串匹配—KMP算法
字符串匹配的应用非常广泛,例如在搜索引擎中,我们通过键入一些关键字就可以得到相关的搜索结果,搜索引擎在这个过程中就使用字符串匹配算法,它通过在资源中匹配关键字,最后给出符合条件的搜索结果。并且我们在使用计算机时,通常需要处理大量的字符串,例如编写C语言时的标准输入和标准输出都是字符串的形式,所以字符串匹配算法是一种非常重要的算法。
在介绍KMP算法之前,先简要说明一下暴力匹配算法。暴力匹配算法的思路非常简单,它通过将模式串与文本串中的子字符串(以文本串的第一个、第二个...以此类推直到第N+1-M个字符开始的长度为M的所有子串)进行比较,最后得到第一次匹配的位置。对于一个长度为N的文本串和长度为M的模式串,暴力匹配算法的时间复杂度为.而KMP算法将时间复杂度减少到了
。
KMP算法是由D.EKnuth、J.H.Morris和V.R.Pratt提出的,时间复杂度为的字符串匹配算法。KMP算法之所以能将时间复杂度减少到这个量级,是因为在KMP算法中充分利用了已匹配成功的部分,使得文本串中的指针不需要像暴力匹配算法一样回溯到正在比较子串的开头(第二个字符,因为如果匹配失败,指针会移动到匹配失败子串的第一个字符的下一个字符并重新开始比较),在比较过程中,文本串的指针一直在向后移动,我们只需要回溯模式串的指针即可。
下面本文将用例子来详细说明KMP算法的思路及过程,假设我们要在文本串acacfacace中找到模式串acace(字符串末尾的\0字符省略)。
在开始之前,首先介绍两个概念,前缀和后缀。字符串的前缀是指不包括字符串最后一个字符的所有子串,对上述例子,a,ac,aca,acac...acacfacac是文本串的所有前缀,后缀就是不包括第一个字符的,从后往前的串,e,ce,ace...cacfacace是文本串的所有后缀。
a | c | a | c | f | a | c | a | c | e |
a | c | a | c | e |
我们先开始从头匹配,匹配成功的部分标为红色,如上图。显然,模式串的最后一个字符与文本串的下一个字符不匹配,那么对于暴力匹配算法,接下来就会将文本串的指针回溯到第一个c字符,模式串的指针回溯到第一个字符。下面列出暴力匹配的一些步骤(用绿色表示接下来要匹配的串,注意标记的不是匹配成功的串,用紫色标记上一次匹配失败的位置):
a | c | a | c | f | a | c | a | c | e |
a | c | a | c | e |
那么可以看到,在f之前,cac与aca仍然不匹配,继续下一步:
a | c | a | c | f | a | c | a | c | e |
a | c | a | c | e |
在这一步中,可以看到两个ac匹配成功了。那么我们仔细观察就会发现,在第一步中已经匹配成功了acac子串,对于下面两步的暴力匹配算法,我们仍然需要拿出第一步中已经匹配成功的文本串的子串的一部分来与模式串进行比较。
显然,文本串中已经匹配成功的算法一定是模式串的子串。那么我们就会发现上面两步暴力匹配算法就是多余的,因为我们不需要比较文本串和模式串,我们只需要考察模式串就会发现cac与aca是不匹配的,也就是说这一步的暴力匹配算法可以删除。
下面我们进一步观察已经匹配的模式串和接下来暴力匹配的部分有什么关系。在上面的例子中,如果我们将已匹配部分看成一个字符串,那么cac和aca就是它的一个后缀和前缀,ac和ac也是它的一个前缀和后缀,也就是说,在暴力匹配算法中的文本串指针还没有移出刚才比较成功的字符串时,文本串和模式串的每次比较实际上都是已匹配部分的前缀和后缀的比较。所以当我们找到模式串中已匹配部分的最长相等前后缀,那么将模式串的指针移动到这个前缀的下一个字符,文本串的指针不用移动,就可以继续比较,因为我们知道模式串指针之前的部分一定是匹配的,并且这个匹配部分一定是最长的。
我们需要建立一个next数组用来表示当模式串中一个位置的元素匹配失败时,模式串的指针应该回溯到什么地方重新比较。这个回溯位置取决于匹配失败元素之前的子串的最长相等前后缀。比如在上面的例子中,acac的最长相等前后缀的长度为2,也就是说,在下一次比较时,模式串的长度为2的前缀已经匹配成功了,所以只需要将指针移动到第三个字符即可。
KMP算法代码:
int KMP(char* t, char* s) {//t为文本串,s为模式串int sl = strlen(s);//模式串的长度int tl = strlen(t);//文本串的长度int i, j;//next数组,next数组标记每个模式串的字符匹配失败要返回的位置,所以元素的个数与模式串长度相同int* next = (int*)malloc(sizeof(int) * sl);if (next == NULL) {perror("malloc");return -1;}//建立next数组j = -1; i = 0;next[0] = -1;//如果不将next值设为-1,那么第一个元素匹配失败时就会回到它自己,这时候我们要将文本串的指针+1,//就需要单独的代码来完成这一部分,将next设为-1,就可以同时将两个指针都+1,这和匹配成功的逻辑是相同的while (i < sl) {//i是快指针,当它越界说明next建立结束//这里的代码逻辑是,建立next数组的过程实际上是模式串的子串中的前后缀匹配过程//i始终指向已处理的子串的最后一个元素,从i=1开始,如果i和j指向的元素相同,那么这两个元素刚好是//i=2元素的前缀和后缀,所以当i=2匹配失败时,将指针移动到j+1,因为0和1位置的元素已经匹配//可以看到i和j始终是同时向后移动的,也就是说,它们始终走过相同的距离,举一个特殊例子来说明这个next建立的原理//如果从j=0,i=1位置开始,i和j指向的元素始终相等,那么当i指向最后一个元素时,j指向倒数第二个元素//这时,从第一个字符到j-1,从第二个字符到i-1,它们分别是已匹配子串的最长相等前缀和后缀,那么当i匹配失败时//显然指针需要移动到j重新比较,这个特殊的例子就说明了i指向位置之前的子串的某个后缀始终是与0到j的前缀相等的//并且是最长的,因为只有满足一定的条件i和j才会同时+1if (j == -1 || s[i] == s[j]) {//将j的初始值设为-1是为了处理当始终不满足后面的条件时,将next的值设为0i++;j++;next[i] = j;}else {j = next[j];//当i和j位置的元素匹配失败时,由于i之前的元素都已经建立了next值,所以j进行回溯重新开始匹配}}//优化next数组//如果模式串中某个元素的next值指向的元素与它相同,那么这个元素匹配失败时,实际上不需要比较next值指向的元素//我们可以对next数组进行优化来避免这种情况//next[0]依然等于-1for (int i = 1; i < sl; i++) {if (s[i] == s[next[i]]) {//如果s[i]和该元素的next值指向的元素相同//那么将该元素的next值设置为其next值指向元素的next值,由于是从前往后处理的,所以新的next值指向的元素一定不等于它本身next[i] = next[next[i]];}}//KMP算法过程i = 0; j = 0;while (i < tl && j < sl) {if (j == -1 || t[i] == s[j]) {//如果需要从模式串的头开始匹配或者对应元素匹配成功i++;j++;}else {j = next[j];//否则回溯j}}if (j == sl) {return i - j;}return -1;
}
相关文章:
字符串匹配—KMP算法
字符串匹配的应用非常广泛,例如在搜索引擎中,我们通过键入一些关键字就可以得到相关的搜索结果,搜索引擎在这个过程中就使用字符串匹配算法,它通过在资源中匹配关键字,最后给出符合条件的搜索结果。并且我们在使用计算…...

【微信小程序】 权限接口梳理以及代码实现
1、权限接口说明 官方权限说明 部分接口需要经过用户授权统一才能调用。我们把这些接口按使用范围分成多个scope,用户选择对scope进行授权,当授权给一个scope之后,其对应的所有接口都可以直接使用。 此类接口调用时: 如…...

【每日一词】leit-motif
1、释义 leit-motif: n. 主乐调;主题;主旨。 复数:leit-motifs 2、例句 Hence the ‘ancient’ rhyme that appears as the leit-motif of The Lord of the Rings, Three Rings for the Elven-Kings under the sky, Seven for the Dwarf-lor…...

windows 环境修改 Docker 存储目录
windows 环境修改存储目录 docker 安装时不提供指定安装路径和数据存储路径的选项,且默认是安装在C盘的。C盘比较小的,等docker运行久了,一大堆的东西放在上面容易导致磁盘爆掉。所以安装前可以做些准备,让安装的实际路径不在C盘&…...

上海市青少年算法月赛丙组—目录汇总
上海市青少年算法2023年3月月赛(丙组) T1 神奇的字母序列 T2 约数的分类 T3 循环播放 T4 数对的个数 T5 选取子段 上海市青少年算法2023年2月月赛(丙组) T1 格式改写 T2 倍数统计 T3 区间的并 T4 平分数字(一…...

手动实现promise.all
手动实现promise.all function promiseAll(promises) {return new Promise((resolve, reject) > {const results [];let count 0;promises.forEach((promise, index) > {Promise.resolve(promise).then(result > {results[index] result;count;if (count promise…...

如何搭建关键字驱动自动化测试框架?这绝对是全网天花板的教程
目录 1. 关键字驱动自动化测试介绍 2. 搭建关键字驱动自动化测试框架 步骤1:选择测试工具 步骤2:定义测试用例 步骤3:编写测试驱动引擎 步骤4:实现测试关键字库 步骤5:执行测试 3. 实现关键字驱动自动化测试的关…...

字符串反转操作
1:将字符串反转 给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母…...

TensorFlow 智能移动项目:1~5
原文:Intelligent mobile projects with TensorFlow 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只…...

[MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条
文章目录 关于图形绘制创建自定义控件使用控件创建专辑封面项目地址 我们将绘制一个圆形的音乐播放控件,它包含一个圆形的进度条、专辑页面和播放按钮。 关于图形绘制 使用MAUI的绘制功能,需要Microsoft.Maui.Graphics库。 Microsoft.Maui.Graphics 是…...

web路径专题+会话技术
目录 自定义快捷键 1. 工程路径问题及解决方案1.1 相对路径1.2 相对路径缺点1.3 base标签1.4 作业11.5 作业21.6注意细节1.7 重定向作业1.8 web工程路径优化 2. Cookie技术2.1 Cookie简单示意图2.2 Cookie常用方法2.2 Cookie创建2.3 Cookie读取2.3.1 JSESSIONID2.3.2 读取指定C…...

Jetpack Compose 实战 宝可梦图鉴
文章目录 前言实现效果一、架构介绍二、一些的功能点的介绍加载图片并获取主色,再讲主色设置为背景一个进度缓慢增加的圆形进度条单Activity使用navigation跳转Compose可组合项返回时页面重组的问题hiltViewModel() 主要参考项目总结 前言 阅读本文需要一定compose基础&#x…...

高效时间管理日历 DHTMLX Event Calendar 2.0.3 Crack
DHTMLX Event Calendar用于高效时间管理的轻量级 JavaScript 事件日历 DHTMLX 可帮助您开发类似 Google 的 JavaScript 事件日历,以高效地组织约会。 用户可以通过拖放来管理事件,并以六种不同的模式显示它们。 JavaScript 事件日历功能 轻的简单的 Java…...

ASIC-WORLD Verilog(2)FPGA的设计流程
写在前面 在自己准备写一些简单的verilog教程之前,参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。 这是网站原文&…...

数字化体验时代,企业如何做好内部知识数字化管理
随着数字化时代的到来,企业内部的知识管理也面临着新的挑战和机遇。数字化技术的应用,可以极大地提高企业内部知识的数字化管理效率和质量,从而提升企业内部的工作效率、员工满意度和企业竞争力。本文将从数字化时代的背景出发,探…...

Qt5.12實戰之Linux靜態庫與動態庫多文件生成a與so文件並調用
1.編輯並輸入內容到test.cpp與test2.cpp test.cpp #include <stdio.h> int func() {return 888; } test2.cpp #include <stdio.h> int func2() {return 999; } 將test.cpp與test2.cpp編譯成目標文件: g -c test.cpp test2.cpp 一次性生成目標文件…...

Spring 之初始化前中后详解
Spring 框架是一个非常流行的 Java 框架,它提供了一种轻量级的、可扩展的方式来构建企业级应用程序。在 Spring 的生命周期中,有三个重要的阶段,即初始化前、初始化、初始化后。这篇文章将详细介绍这些阶段,并提供相应的源代码示例…...

企业数字化转型路上的陷阱有哪些
近年来,随着科技的快速发展,越来越多的企业开始了数字化转型的征程,希望通过数字化技术来提高企业的效率、降低成本、提升竞争力。然而,数字化转型也存在许多陷阱,如果不注意,可能会导致企业陷入困境。下面…...

Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++)
Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C) Baumer工业相机Baumer工业相机使用图像算法增加图像的技术背景Baumer工业相机通过BGAPI SDK联合OpenCV使用图像增强算法1.引用合适的类文件2.BGAPI SDK在图像回调中引用…...

面试官:“你会组件化开发操作吗?它的优势在哪?”
随着 Android 版本的不断更新升级和用户对 APP 产品需求技术越来越高,相对的各大公司对 Android 开发者们设置的招聘门槛也越来越高。 至于如何去看一个开发者水平的高低,一般看面试官会怎么问,会问哪些部分的技术内容? 一般公司…...

腾讯新增长,AI扛大旗?
经历了疫情期间的低谷与波折,腾讯正在恢复它的活力。 3月22日,腾讯发布了2022年第四季度及全年财报。财报显示,2022全年营收为5546亿元人民币,归母净利润(Non-IFRS)为1156亿元人民币;2022年腾讯第四季度的营收为1450亿…...

项目6:实现数据字典的展示与缓存
项目6:实现数据字典的展示与缓存 1.数据字典如何展示? 2.前后端如何设计? 3.前端设计代码? 4.后端设计代码? 5.实现数据字典缓存到redis 项目6:实现数据字典的展示与缓存 1.数据字典如何展示? ①数据字典展示 树形结构②...

JsNode算法题acm模式输入
js分为jsNode和JsV8两种输入输出方式,一般的执行代码还是一样的 牛客是两种都支持 华为的题目大多只支持jsNode 本文主要介绍jsNode的输入 JsNode 首先他是逐行输入的,就和py差不多,一定是每行每行地输入,用字符串line&#x…...

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加
Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 目录 Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 0.创建数据库 1. 在resources目录下创建db.properties文件 2. /** * 获取链接与释放资源的工具类--JdbcUtil类 */ 3…...

(十七)排序算法-基数排序
1 基本介绍 1.1 概述 (1)基数排序(radix sort)属于“分配式排序”,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。 (2&#x…...

JMM之先行发生原则(happens-before)详解
1、概述 在JMM规范下,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happers-before(先行发生)原则。 例如 int x 10 ; int y x; 这两行代码中第二个操作 yx ,因为需要将x值赋值给y,所以第一行代码的…...

含分布式电源的配电网可靠性评估研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

安全加固服务是什么?哪些行业需要做?
安全加固服务是什么?安全加固服务是一种针对企业信息系统、网络设备、应用程序等进行安全加固和优化的服务。安全加固服务的主要目的是保障企业信息系统的安全性和稳定性,有效防范各类网络攻击和安全威胁。 安全加固服务是什么?通常包括以下…...

好程序员:Java书籍推荐,程序员必看的5本Java书籍,赶紧收藏!
今天好程序员给大家推荐5本Java书籍,各大高校都在使用(具体名单如下),所有学习Java的程序员都不应该错过! 第一本Java书籍《Java EE(SSM框架)企业应用实战》 本书全面介绍了JavaEE中MyBatis、Sp…...

maven将jar包添加到本地仓库
第一步:下载需要添加的jar包 可以在maven库中查找下载,也可以在对应官网下载 maven库网址:https://mvnrepository.com/ 找到对应版本的jar包下载 第二步:将下载的jar包放到指定位置(位置自己指定)…...