当前位置: 首页 > 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; 一般公司…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...