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

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...