【优选算法】—— 字符串匹配算法

在本期的字符串匹配算法中,我将给大家带来常见的两种经典的示例:

  • 1、暴力匹配(BF)算法
  • 2、KMP算法

目录

(一)暴力匹配(BF)算法

1、思想

2、演示

3、代码展示

(二)KMP算法

1、思想

2、演示

1️⃣ BF和KMP的区别

 2️⃣  K 的值求解

3️⃣ 求next数组的练习

3、代码展示

4、next数组的优化

(三)总结


(一)暴力匹配(BF)算法

1、思想

BF 算法,即 暴力 (Brute Force) 算法 ,是普通的模式匹配算法, BF 算法的思想:
  1. 就是将目标串S的第一个字符与模式串T 的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;
  2. 若不相等,则比较S的第二个字符和 T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

2、演示

大家看到上诉这段话时肯定是晦涩难懂的,需要例子支持
下面我们就通过例子来解释这个问题:
  • 假定我们给出字符串ababcabcdabcde作为主串, 然后给出子串:abcd”;
  • 现在我们需要查找子串是否在主串中出现,出现返回主串中的第一个匹配的下标,失败返回-1 

  •  只要在匹配的过程当中,匹配失败,那么:i回退到刚刚位置的下一个,j回退到0下标重新开始

3、代码展示

int BF(char *str,char *sub) //str:主串 sub:子串
{assert(str != NULL && sub != NULL);if(str == NULL || sub == NULL){return -1;}int i = 0;int j = 0;int strLen = strlen(str);int subLen = strlen(sub);while(i < strLen && j < subLen){if(str[i] == sub[j]){i++;j++;}else{//回退i = i-j+1;j = 0;}}if(j >= subLen){return i-j;}return -1;
}
int main()
{printf("%d\n",BF("ababcabcdabcde","abcd"));printf("%d\n",BF("ababcabcdabcde","abcde"));printf("%d\n",BF("ababcabcdabcde","abcdef"));return 0;
}

时间复杂度分析:最坏为 O(m*n); m 是主串长度, n 是子串长度

(二)KMP算法

1、思想

KMP 算法是一种改进的字符串匹配算法,由 D.E.Knuth J.H.Morris V.R.Pratt 提出的,因此人们称它为克努特 莫里斯— 普拉特操作(简称 KMP 算法)。 KMP 算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next() 函数实现, 函数 本身包含了模式串的局部匹配信息。 KMP 算法的 时间复杂度 O(m+n) [1]   。 来自 ------- 百度百科。

2、演示

1️⃣ BF和KMP的区别

区别 KMP BF 唯一不一样的地方在,我主串的 i 并不会回退,并且 j 也不会移动到 0 号位置

 首先举例,为什么主串不回退?

 的回退位置

💨 针对上述这样的情况,我们就需要引出next数组

KMP 的精髓就是 next 数组:也就是用 next[j] = k;来表示,不同的  j 来对应一个 K 值, 这个 K 就是你将来要移动的 j 要移动的位置。

 2️⃣  K 的值求解

  • 1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1 下标字符结尾。
  • 2、不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始

3️⃣ 求next数组的练习

练习 1: 举例对于”ababcabcdabcde”, 求其的 next 数组?

 到这里大家对如何求next数组应该问题不大了,接下来的问题就是,已知next[i] = k;怎么求next[i+1] = ?

如果我们能够通过 next[i] 的值 , 通过一系列转换得到 next[i+1] 得值,那么我们就能够实现这部分。
那该怎么做呢?

  1. 首先假设: next[i] = k 成立,那么,就有这个式子成立: P0...Pk-1 = Px...Pi-1;得到: P0...Pk-1 = Pi-k..Pi-1;
  2. 到这一步:我们再假设如果 Pk = Pi;我们可以得到 P0...Pk = Pi-k..Pi;那这个就是 next[i+1] = k+1;

 那么: Pk != Pi 呢?

 

3、代码展示

void GetNext(int *next,const char *sub)
{int lensub = strlen(sub);next[0] = -1;next[1] = 0;int i = 2;//下一项int k = 0;//前一项的Kwhile(i < lensub)//next数组还没有遍历完{if((k == -1) || sub[k] == sub[i-1]){next[i] = k+1;i++;k++;//k = k+1???//下一个K的值新的K值}else{k = next[k];}}
}int KMP(const char *s,const char *sub,int pos)
{int i = pos;int j = 0;int lens = strlen(s);int lensub = strlen(sub);int *next = (int *)malloc(lensub*sizeof(int));//和子串一样长assert(next != NULL);GetNext(next,sub);while(i < lens && j < lensub){if((j == -1) || (s[i] == sub[j])){i++;j++;}else{j = next[j];}}free(next);if(j >= lensub){return i-j;}else{return -1;}
}int main()
{char *str = "ababcabcdabcde";char *sub = "abcd";printf("%d\n",KMP(str,sub,0));return 0;
}

4、next数组的优化

next 数组的优化,即如何得到 nextval 数组:有如下串:
  1. aaaaaaaab;
  2. next 数组是-1,0,1,2,3,4,5,6,7
nextval 是:-1, -1, -1, -1, -1, -1, -1, -1, 7
💨  为什么出现修正后的数组,假设在 5 号处失败了,那退一步还是 a,还是相等,接着退还是 a
练习
模式串 t=‘abcaabbcabcaabdab’ ,该模式串的 next 数组的值为( D ) , nextval 数组的值为 ( F
A 0 1 1 1 2 2 1 1 1 2 3 4 5 6 7 1 2
B 0 1 1 1 2 1 2 1 1 2 3 4 5 6 1 1 2
C 0 1 1 1 0 0 1 3 1 0 1 1 0 0 7 0 1
D 0 1 1 1 2 2 3 1 1 2 3 4 5 6 7 1 2
E 0 1 1 0 0 1 1 1 0 1 1 0 0 1 7 0 1
F 0 1 1 0 2 1 3 1 0 1 1 0 2 1 7 0 1


(三)总结

BF(Brute Force)和KMP(Knuth-Morris-Pratt)算法是两种字符串匹配算法,它们的主要区别在于匹配过程中的策略和效率

  1. BF算法:

    • BF算法是一种简单直接的字符串匹配算法,也被称为朴素算法。
    • 它的思想是从主串中的每个位置开始,逐个比较主串和模式串的字符,直到找到匹配或遍历完所有可能位置。
    • 当字符不匹配时,BF算法通过移动主串指针,重新从下一个位置开始匹配。
    • BF算法的时间复杂度为O(m*n),其中m为主串长度,n为模式串长度,最坏情况下需要遍历所有可能的匹配位置。
    • 由于BF算法每次只移动一位,对于大规模文本匹配效率较低。
  2. KMP算法:

    • KMP算法是一种高效的字符串匹配算法,通过利用已知信息减少不必要的字符比较次数。
    • 它利用模式串自身的特性,在匹配过程中跳过一部分已经匹配的字符,从而提高匹配效率。
    • KMP算法包括两个主要步骤:构建最长公共前后缀数组(next数组)和匹配过程。
    • 最长公共前后缀数组(next数组)记录了模式串中对于每个位置,匹配失败时应该跳过的字符数。
    • 在匹配过程中,当字符不匹配时,KMP算法通过查询next数组获取跳跃位置,而不是重新从头开始比较。
    • KMP算法的时间复杂度为O(m+n),其中m为主串长度,n为模式串长度,通过next数组的优化,可以在匹配过程中跳过一定数量的比较,从而提高效率。

以下是关于上述两种算法的小结:

  1. 总结起来,BF算法是一种简单直接的字符串匹配算法,逐个比较字符并逐位移动,效率较低;
  2. 而KMP算法是一种高效的字符串匹配算法,通过构建最长公共前后缀数组和跳跃匹配位置,减少不必要的字符比较次数,提高匹配效率。
  3. 因此,在大规模文本匹配的应用中,KMP算法通常比BF算法更加高效。

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

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

相关文章

基于Roop视频换脸

Roop 是一个强大的一键换脸工具&#xff0c;允许用户在视频中替换面部&#xff0c;只需要目标面部的一张图片&#xff0c;无需数据集&#xff0c;无需训练。 相对于之前的 Simswap 来说效果要好很多&#xff0c;不过需要注意的是没有授权不要商用。 文章目录 环境搭建使用方法…

Java之AbstractQueuedSynchronizer

要让你写一个java版的并发同步库&#xff0c;你会怎么思考设计&#xff1f;&#xff1f;&#xff1f;先思考三五分钟 请先拜读下老外的paperhttp://gee.cs.oswego.edu/dl/papers/aqs.pdf 1. 简介 AbstractQueuedSynchronizer&#xff0c;简称AQS&#xff0c;中文翻译为抽象队…

Git 原理与使用

1.版本控制器 所谓的版本控制器&#xff0c;就是能让你了解到⼀个⽂件的历史&#xff0c;以及它的发展过程的系统。通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统&#xff0c;同时也⽅便多⼈协同作业。 ⽬前最主流的版本控制器就是 Git 。Git 可以控制电脑…

vue 简单实验 v-bind 变量与html属性绑定

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"bind-attribute"><span v-bind:title"message">鼠标悬停几秒钟查看此处动态绑定的提示信息&#xff01;</sp…

实现一个简单的C++线程池

C线程池的优势与劣势 C线程池是指在程序中预先创建一组线程&#xff0c;然后在需要执行任务时&#xff0c;将任务分配给这些线程执行。线程池具有以下优势&#xff1a; 减少创建和销毁线程的开销&#xff1a;创建和销毁线程是需要消耗系统资源的&#xff0c;而线程池中的线程…

Java进阶篇--IO流的第二篇《多样的流》

目录 Java缓冲流 BufferedReader和BufferedWriter类 Java随机流 Java数组流 字节数组流 ByteArrayInputStream流的构造方法&#xff1a; ByteArrayOutputStream流的构造方法&#xff1a; 字符数组流 Java数据流 Java对象流 Java序列化与对象克隆 扩展小知识&#x…

【附安装包】CIMCO Edit 8安装教程|数控编程必备

软件下载 软件&#xff1a;CIMCO Edit版本&#xff1a;8语言&#xff1a;简体中文大小&#xff1a;249.18M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baid…

学习JAVA打卡第四十一天

字符串与字符数组、字节数组 ⑴字符串与字符数组 String类的构造方法String&#xff08;char a[]&#xff09;和String&#xff08;char a[]&#xff09;,int offset,int length,分别用数组a中的全部字符和部分字符创建string对象。 String类也提供将string对象的字符序列存…

Docker常见配置实验

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 1、拉取mysql5.6与owncloud的镜像 docker pull mysql:5.6 docker pull mysql:5.6 2、生成容器实例&#xff0c;构建个人网盘 docker run -d --name mydb1 --env MYSQL_ROOT_PASSWORD123456 mysql:5.6 docker …

关于Java中@Transient主键的作用的一些介绍

Transient主下面是关于Transient主键的使用方法、代码案例以及与transient关键字的区别&#xff0c;以及一些实用场景的详细介绍。 1. Transient主键的作用 在实体类中&#xff0c;通常需要将某些字段标记为主键&#xff0c;并将其映射到数据库中的主键字段。但是&#xff0c…

mybatisplus查询报Invalid bound statement (not found)

问题描述 在复现一个老项目时&#xff0c;使用了mybatisplus&#xff0c;其版本为3.1.1。 结果项目启动成功&#xff0c;但是在使用mapper查询时&#xff0c;报了如下错误&#xff1a;Invalid bound statement (not found) 项目的mapper的xml文件是放在resources/mapper目录下…

mac清理磁盘空间软件有哪些 mac清理磁盘空间怎么清理

随着时间的推移&#xff0c;Mac电脑上的文件会越来越多&#xff0c;很快就会占满磁盘空间。这时候&#xff0c;我们需要一个好的Mac清理磁盘空间软件来释放空间&#xff0c;保持电脑的良好性能。那么&#xff0c;mac清理磁盘空间软件有哪些呢&#xff1f;接下来&#xff0c;我将…

如何维护自己的电脑的措施

维护自己的电脑可以采取以下措施&#xff1a; 硬件维护&#xff1a;定期清理电脑表面的灰尘和污垢&#xff0c;避免灰尘对电脑内部部件造成影响。电源插座要保持接触良好&#xff0c;保证电脑的电源稳定。如果使用笔记本电脑&#xff0c;要注意保证散热通畅&#xff0c;避免电…

设计模式(单例模式,工厂模式),线程池

目录 什么是设计模式? 单例模式 饿汉模式 懒汉模式 工厂模式 线程池 线程池种类 ThreadPoolExcutor的构造方法: 手动实现一个线程池 什么是设计模式? 计算机行业程序员水平层次不齐,为了让所有人都能够写出规范的代码,于是就有了设计模式,针对一些典型的场景,给出一…

java+springboot+mysql村务档案管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的村务档案管理系统&#xff0c;系统包含超级管理员、工作人员角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;系统用户管理&#xff08;工作人员管理&#xff09;&#xff1b;公开资料&#xff1b;会议记录&…

A Survey on Knowledge-Enhanced Pre-trained Language Models

摘要 自然语言处理(NLP)已经通过使用BERT等预训练语言模型(plm)发生了革命性的变化。尽管几乎在每个NLP任务中都创造了新的记录,但plm仍然面临许多挑战,包括可解释性差,推理能力弱,以及在应用于下游任务时需要大量昂贵的注释数据。通过将外部知识集成到plm中,知识增强预训…

.NET 最便捷的Log4Net日志记录器

最便捷的Log4Net使用方法 LOG4NET 配置日志记录器开始引用nuget LOG4NET 配置日志记录器 Apache log4net 库是一个帮助程序员将日志语句输出到各种的工具 的输出目标。log4net是优秀的Apachelog4j™框架的移植 Microsoft.NET 运行时。我们保持了与原始log4j相似的框架 同时利…

Rust处理JSON

基本操作 Cargo.toml: [package]name "json"version "0.1.0"edition "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]serde { version "1", features …

前端(十四)——DOM节点操作手册:你需要了解的一切

&#x1f642;博主&#xff1a;小猫娃来啦 &#x1f642;文章核心&#xff1a;DOM节点操作手册&#xff1a;你需要了解的一切 文章目录 前言DOM基础知识操作现有节点创建新节点遍历节点树修改节点属性和样式事件处理实践应用动态创建表格动态更新列表 前言 DOM&#xff08;文档…

算法岗和开发岗有什么区别?

算法岗和开发岗有什么区别&#xff1f; ​ 算法岗位和开发岗位在实际应用中有很大的差异&#xff0c;而且其工作的内容重心也不一样。企业对职位能力的要求也是存在着很大的区别。 ​ 其实在真正的实践中&#xff0c;只有大厂才对这两个岗位分的比较清楚&#xff0c;小的公司…