当前位置: 首页 > news >正文

字符串匹配—KMP算法

字符串匹配的应用非常广泛,例如在搜索引擎中,我们通过键入一些关键字就可以得到相关的搜索结果,搜索引擎在这个过程中就使用字符串匹配算法,它通过在资源中匹配关键字,最后给出符合条件的搜索结果。并且我们在使用计算机时,通常需要处理大量的字符串,例如编写C语言时的标准输入和标准输出都是字符串的形式,所以字符串匹配算法是一种非常重要的算法。

在介绍KMP算法之前,先简要说明一下暴力匹配算法。暴力匹配算法的思路非常简单,它通过将模式串与文本串中的子字符串(以文本串的第一个、第二个...以此类推直到第N+1-M个字符开始的长度为M的所有子串)进行比较,最后得到第一次匹配的位置。对于一个长度为N的文本串和长度为M的模式串,暴力匹配算法的时间复杂度为O(N*M).而KMP算法将时间复杂度减少到了O(N+M)

KMP算法是由D.EKnuth、J.H.Morris和V.R.Pratt提出的,时间复杂度为O(N+M)的字符串匹配算法。KMP算法之所以能将时间复杂度减少到这个量级,是因为在KMP算法中充分利用了已匹配成功的部分,使得文本串中的指针不需要像暴力匹配算法一样回溯到正在比较子串的开头(第二个字符,因为如果匹配失败,指针会移动到匹配失败子串的第一个字符的下一个字符并重新开始比较),在比较过程中,文本串的指针一直在向后移动,我们只需要回溯模式串的指针即可。

下面本文将用例子来详细说明KMP算法的思路及过程,假设我们要在文本串acacfacace中找到模式串acace(字符串末尾的\0字符省略)。

在开始之前,首先介绍两个概念,前缀和后缀。字符串的前缀是指不包括字符串最后一个字符的所有子串,对上述例子,a,ac,aca,acac...acacfacac是文本串的所有前缀,后缀就是不包括第一个字符的,从后往前的串,e,ce,ace...cacfacace是文本串的所有后缀。

acacfacace
acace

我们先开始从头匹配,匹配成功的部分标为红色,如上图。显然,模式串的最后一个字符与文本串的下一个字符不匹配,那么对于暴力匹配算法,接下来就会将文本串的指针回溯到第一个c字符,模式串的指针回溯到第一个字符。下面列出暴力匹配的一些步骤(用绿色表示接下来要匹配的串,注意标记的不是匹配成功的串,用紫色标记上一次匹配失败的位置):

acacfacace
acace

 那么可以看到,在f之前,cac与aca仍然不匹配,继续下一步:

acacfacace
acace

在这一步中,可以看到两个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算法

字符串匹配的应用非常广泛&#xff0c;例如在搜索引擎中&#xff0c;我们通过键入一些关键字就可以得到相关的搜索结果&#xff0c;搜索引擎在这个过程中就使用字符串匹配算法&#xff0c;它通过在资源中匹配关键字&#xff0c;最后给出符合条件的搜索结果。并且我们在使用计算…...

【微信小程序】 权限接口梳理以及代码实现

​ 1、权限接口说明 官方权限说明   部分接口需要经过用户授权统一才能调用。我们把这些接口按使用范围分成多个scope&#xff0c;用户选择对scope进行授权&#xff0c;当授权给一个scope之后&#xff0c;其对应的所有接口都可以直接使用。 此类接口调用时&#xff1a; 如…...

【每日一词】leit-motif

1、释义 leit-motif: n. 主乐调&#xff1b;主题&#xff1b;主旨。 复数&#xff1a;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 安装时不提供指定安装路径和数据存储路径的选项&#xff0c;且默认是安装在C盘的。C盘比较小的&#xff0c;等docker运行久了&#xff0c;一大堆的东西放在上面容易导致磁盘爆掉。所以安装前可以做些准备&#xff0c;让安装的实际路径不在C盘&…...

上海市青少年算法月赛丙组—目录汇总

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

手动实现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&#xff1a;选择测试工具 步骤2&#xff1a;定义测试用例 步骤3&#xff1a;编写测试驱动引擎 步骤4&#xff1a;实现测试关键字库 步骤5&#xff1a;执行测试 3. 实现关键字驱动自动化测试的关…...

字符串反转操作

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

TensorFlow 智能移动项目:1~5

原文&#xff1a;Intelligent mobile projects with TensorFlow 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只…...

[MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条

文章目录 关于图形绘制创建自定义控件使用控件创建专辑封面项目地址 我们将绘制一个圆形的音乐播放控件&#xff0c;它包含一个圆形的进度条、专辑页面和播放按钮。 关于图形绘制 使用MAUI的绘制功能&#xff0c;需要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 事件日历&#xff0c;以高效地组织约会。 用户可以通过拖放来管理事件&#xff0c;并以六种不同的模式显示它们。 JavaScript 事件日历功能 轻的简单的 Java…...

ASIC-WORLD Verilog(2)FPGA的设计流程

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

数字化体验时代,企业如何做好内部知识数字化管理

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

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編譯成目標文件&#xff1a; g -c test.cpp test2.cpp 一次性生成目標文件…...

Spring 之初始化前中后详解

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

企业数字化转型路上的陷阱有哪些

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

Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++)

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

面试官:“你会组件化开发操作吗?它的优势在哪?”

随着 Android 版本的不断更新升级和用户对 APP 产品需求技术越来越高&#xff0c;相对的各大公司对 Android 开发者们设置的招聘门槛也越来越高。 至于如何去看一个开发者水平的高低&#xff0c;一般看面试官会怎么问&#xff0c;会问哪些部分的技术内容&#xff1f; 一般公司…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...