BF算法,KMP算法

前言:今天我们来学习两种算法,BF算法和KMP算法。相信会让许多小伙伴们打开新世界的大门。

1 BF算法

实践是检验真理的唯一标准。举一个例子说明BF算法。现在我们要在一个主串中找子串的位置。那我们该如何解决这个问题呢?最简单的办法自然是使用C语言中的库函数strstr。

#include<stdio.h>
#include<string.h>
int main()
{char str1[20] = "abbbcdef";char str2[20] = "bbc";char* ret = strstr(str1, str2);char* ps = strncpy(ret, "hello", 5);printf("%s\n", str1);return 0;
}

但现在我们不使用C语言中的库函数strstr,是否有办法可以解决呢?当然是有的。

#include<stdio.h>
#include<assert.h>
#include<string.h>
char* BF(char* str1, char* str2)
{assert(str1 != NULL && str2 != NULL);char* s1 = str1;//遍历主串char* s2 = str2;//遍历子串char* cur = str1;//记录可能开始匹配的位置while (*cur){//完成一次匹配s1 = cur;s2 = str2;while (*s1 && *s2 && *s1 == *s2){s1++;s2++;}if (*s2 == '\0'){return cur;}cur++;}return NULL;
}
int main()
{char str1[20] = "abbbcdef";char str2[20] = "bbc";char* ret = BF(str1, str2);char* ps = strncpy(ret, "world", 5);printf("%s\n", str1);
}

画图分析

*s1=a,*s2=b。此时 *s1 != *s2,说明从cur这个位置匹配失败,那么下一次就要从cur++的位置开始匹配,同时将s1的位置更改为新的cur

在这里插入图片描述


*s1=b,*s2=b。此时*s1==*s2,说明从当前cur的位置是有可能匹配成功的。那么就执行s1++,s2++的操作,比较下一对字符的内容

在这里插入图片描述


在这里插入图片描述


*s1=b,*s2=c。此时*s1!=*s2,说明从cur的位置匹配失败,进一步更新cur的位置,同时将s1的位置更新为新的cur的位置,s2回到起始位置。重新进行下一次的匹配

在这里插入图片描述


更新之后新的cur和s1的位置以及s2的位置

在这里插入图片描述


这里省略了相同的步骤,直接演示最后的结果。

在这里插入图片描述

这就是所谓的BF算法。大家是否理解了呢?接下来就让我们一起来学习更加高深莫测的KMP算法。大家做好准备哦。

2 KMP算法

依然是在主串中找寻子串的位置。这一次使用KMP算法实现。

什么是KMP算法呢?

KMP算法是一种改进的字符串匹配算法KMP算法的核心思想是利用主串与模式串(子串)匹配失败后的信息,尽量减少模式串(子串)与主串匹配的次数以达到快速匹配的目的。具体实现是通过一个next数组实现,数组本身包含了子串的局部匹配信息。KMP算法的时间复杂度O(m+n)

KMP算法与BF算法的区别是:主串的i并不会回退,子串的j不一定回退到0位置

画图分析

从当前 i 的位置开始匹配,匹配成功就执行 i++,j++的操作,接着匹配下一对字符的内容

在这里插入图片描述


这里省略了相同的步骤,直接演示最后的结果。此时匹配失败,i不会进行回退,j回退到一个随机的位置

在这里插入图片描述

该回退到哪里呢?这是一个值得深思的问题。在这个位置匹配失败就意味着 i 前面和 j 前面有一部分是相同的。那我们来研究一下 j 到底该回退到哪里呢?

在这里插入图片描述

假设1与3的内容相同,2与3的内容相同。那么1与2的内容就相同。如果此时下标 i 与下标 j 的内容不匹配,j 应该回退到哪里去呢?j 应该回退到子串中下标为2的位置去。为什么呢?还记得KMP算法的核心思想吗。需要尽量减少模式串与主串匹配的次数以达到快速匹配的目的1与3的内容相同,就没有必要再进行重复的匹配,因此 j 应该回退到子串中下标为2的位置。如果依然匹配失败,就继续回退

那新的问题又来了,我们要怎么得到回退的位置呢?根据上面的图分析,假设子串叫做p,如果p[0]~p[1]之间的内容等于p[3]~p[4]之间的内容,我们会发现相等部分的长度为2,刚好是 j 要回退的位置。那不就出来了。next数组的作用就是保存子串中某个位置匹配失败后,要回退的位置

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

k值的求法:

. 找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标0的字符开始,另一个以下标为 j-1 的字符结尾。这两个真子串的长度就是k值

. 不管什么数据,next[0]=-1,next[1]=0

接下来的问题就是已知next[j-1]=k,如何求next[j]=?

在这里插入图片描述


在这里插入图片描述

next数组的特点:相邻的k值如果是增加的,一定是逐步加1的过程

KMP算法的实现

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
//str代表主串
//sub代表子串
//pos代表主串开始匹配的位置
void GetNext(int* next, char* sub, int LenSub)
{assert(next != NULL && sub != NULL && LenSub);next[0] = -1;next[1] = 0;int i = 2;int k = 0;//保存前一项的k值while (i < LenSub){if (-1 == k || sub[i - 1] == sub[k]){next[i] = k + 1;i++;k++;}else{k = next[k];//有可能回退到-1,子串进行访问时会出现越界访问}}
}
int KMP(char* str, char* sub, int pos)
{assert(str != NULL && sub != NULL);int LenStr = (int)strlen(str);int LenSub = (int)strlen(sub);if (0 == LenStr || 0 == LenSub){return -1;}if (pos < 0 || pos >= LenStr){return -1;}int* next = (int*)malloc(sizeof(int) * LenSub);if (NULL == next){perror("空间开辟失败的原因是");return -1;}GetNext(next, sub, LenSub);int i = pos;//遍历主串int j = 0;//遍历子串while (i < LenStr && j < LenSub){if (-1 == j || str[i] == sub[j]){i++;j++;}else{j = next[j];//有可能回退到-1,子串进行访问时会出现越界访问}}if (j >= LenSub){return i - j;}else{return -1;}free(next);next = NULL;
}
int main()
{printf("%d\n", KMP("abbbcdefg", "bbc", 0));//2printf("%d\n", KMP("abbcdef", "ab", 0));//0printf("%d\n", KMP("abccdef", "bcd", 0));//-1printf("%d\n", KMP("abbcdabcdef", "abc", 0));//5printf("%d\n", KMP("abbccddef", "abcde", 0));//-1printf("%d\n", KMP("abcddef", "ab", 0));//0return 0;
}

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

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

相关文章

Repeat方法:取模运算教材与Unity控制台输出数值不同的原因

学习该知识点的参考教材&#xff1a;Unity API解析/陈宏泉编著.——北京&#xff1a;人民邮电出版社&#xff0c;2014.9 编辑脚本的环境&#xff1a;Visual Studio 2022 在学习该本教材的第五章Mathf类的内容&#xff0c;通过跟随教材上的代码了解不同UnityAPI的具体用法时&a…

【机器学习】线性回归与逻辑回归的极致解析:从数学理论到实战案例

文章目录 1. 引言Python 代码示例 2. 线性回归2.1 线性回归的基本概念线性回归的定义数学表达式及模型假设 2.2 线性回归的工作原理最小二乘法&#xff08;Ordinary Least Squares, OLS&#xff09;梯度下降法在线性回归中的应用多元线性回归与一元线性回归的区别与联系 2.3 线…

什么是AR、VR、MR、XR?

时代背景 近年来随着计算机图形学、显示技术等的发展&#xff0c;视觉虚拟化技术得到了广泛的发展&#xff0c;并且越来越普及化&#xff0c;慢慢的也走入人们的视野。目前市场上视觉虚拟化技术的主流分为这几种 VR、AR、MR、XR。这几项技术并不是最近才出现的&#xff0c;VR的…

linux开通端口命令

这块需要开通8088 8083 端口限制 查看已开放端口&#xff1a;sudo firewall-cmd --list-ports 开放8083端口&#xff1a;sudo firewall-cmd --add-port8088/tcp --permanent 开放8088端口&#xff1a;sudo firewall-cmd --add-port8083/tcp --permanent 移除端…

机器学习笔记 第十二章计算学习理论

12.1 基础知识 计算学习理论就是关于机器学习的理论基础&#xff0c;其作用就是分析学习任务的困难实质&#xff0c;通过分析结果来知道算法设计&#xff0c;并为学习算法提供理论保证。 给定样例集&#xff0c;&#xff0c;假设为二分类问题&#xff0c;一般。假定中的所有样…

实现将docx转成PDF

最近实现了一个将docx转成PDF的功能&#xff0c;这里来记录一下实现过程 我是参考这篇文章Java将Word转换成PDF的常用用法_java_脚本之家 实现步骤基本上是按照上面文档中描述的内容&#xff0c;把大象装冰箱一共就三步 1、导入依赖 <?xml version"1.0" enco…

GoMail发送邮件的性能优化策略有哪些方法?

GoMail发送邮件如何配置服务器&#xff1f;GoMail发信功能如何&#xff1f; GoMail是一款广受欢迎的Go语言邮件发送库&#xff0c;具备高效、易用等优点&#xff0c;但在高并发场景下&#xff0c;GoMail发送邮件的性能优化显得尤为重要。AokSend将探讨几种有效的GoMail发送邮件…

MySQL中处理JSON数据:大数据分析的新方向

1. 简介 1.1. 概述 在MySQL中处理JSON数据的能力是在MySQL 5.7版本中引入的,并在后续的版本中不断得到增强。这使得MySQL能够直接操作和查询JSON格式的数据,极大地扩展了其处理复杂数据结构的能力。 1.2. 主要特点 灵活性与可扩展性 :JSON允许开发者存储不规则和嵌套的数…

每日一问:深入探讨TCP与UDP的区别

每日一问&#xff1a;TCP与UDP区别的深入探讨 在网络通信中&#xff0c;TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是最常用的传输层协议。本文将详细探讨这两者的基本概念、功能差异及其在实际应用中的使用场景。通过实例和代码演示…

PDF Shaper Ultimate v14.4 中文授权版

如今对PDF处理的软件很多都是只是单一的功能。PDF Shaper给你完全不同的体验&#xff0c;因为PDF Shaper是一款PDF工具集合的软件。有了PDF Shaper&#xff0c;你以后再也不用下载其他处理PDF的软件了。PDF Shaper的功能有&#xff1a;合并&#xff0c;分割&#xff0c;加密和解…

贪吃蛇(C语言详解)

贪吃蛇游戏运行画面-CSDN直播 目录 贪吃蛇游戏运行画面-CSDN直播 1. 实验目标 2. Win32 API介绍 2.1 Win32 API 2.2 控制台程序&#xff08;Console&#xff09; 2.3 控制台屏幕上的坐标COORD 2.4 GetStdHandle 2.5 GetConsoleCursorlnfo 2.5.1 CONSOLE_CURSOR_INFO …

ant design pro access.ts 是如何控制多角色的权限的

ant design pro 如何去保存颜色ant design pro v6 如何做好角色管理ant design 的 tree 如何作为角色中的权限选择之一ant design 的 tree 如何作为角色中的权限选择之二 看上面的图片&#xff0c;在前端中如何控制这些权限&#xff0c;比如控制按钮的显示&#xff0c;还有菜单…

【Linux操作系统】进程控制

目录 一、进程创建1.1 认识fork1.2 写时拷贝 二、进程终止2.1 进程退出2.2 函数退出2.3 exit 三、进程等待四、程序替换 一、进程创建 1.1 认识fork fork函数是系统调用接口&#xff0c;用来创建子进程的 根据进程的pid&#xff0c;可以看出父进程fork后分为父进程和子进程…

C++ //练习 17.12 使用前一题中的数据结构,编写一个函数,它接受一个问题编号和一个表示真/假解答的值,函数根据这两个参数更新测验的解答。

C Primer&#xff08;第5版&#xff09; 练习 17.12 练习 17.12 使用前一题中的数据结构&#xff0c;编写一个函数&#xff0c;它接受一个问题编号和一个表示真/假解答的值&#xff0c;函数根据这两个参数更新测验的解答。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器…

【pdf文件生成】如何将盖章的文件生成PDF文件

一、提出问题 在我们的工作中&#xff0c;有时候上级让下级将盖章的文件生成PDF文件通过内部平台发送到上级邮箱&#xff0c;那如何解决呢&#xff1f;是去找一个扫描仪&#xff0c;还是用手机拍图转。用Python基实就能实现。 二、分析问题 现在网上好多的软件都是收费的&am…

Spring 中AbstractApplicationContext

AbstractApplicationContext 是 Spring Framework 中的一个抽象类&#xff0c;位于 org.springframework.context 包中。它带有一系列的实现&#xff0c;主要用于为 Spring 的应用上下文提供基本的功能。所有的 Spring 应用上下文实现&#xff08;如 ClassPathXmlApplicationCo…

vue3列表页搜索条件封装

搜索框组件 封装常用搜索框组件&#xff0c;类型有&#xff1a; input&#xff08;默认值)selectselectV2 (value/label键值对数组)datePickeryear 集成新增、修改、删除、导入、导出按钮&#xff0c;支持slot自定义其他按钮封装搜索、重置按钮封装按钮权限封装导入弹框 本例仅…

找到你的任务管理伙伴:待办事项软件终极指南

国内外主流的10款待办事项管理软件对比&#xff1a;PingCode、WorktileTodoist、TickTick、Teambition、 Microsoft To Do、. Asana、Tower、番茄ToDo、飞书。 在面对日益复杂的工作和个人任务时&#xff0c;找到一款能够有效帮助你管理日常待办事项的软件&#xff0c;变得越来…

Baumer工业相机堡盟工业相机如何通过BGAPISDK初始化时过滤其它非Baumer相机(C#)

Baumer工业相机堡盟工业相机如何通过BGAPISDK初始化时过滤其它非Baumer相机&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机通过SDK初始化时过滤其它非Baumer相机的技术背景通过SDK过滤其它非Baumer相机的代码分析1、引用合适的类文件2、初始化时过滤其它非Baumer相机…

[RCTF2015]EasySQL1

打开题目 点进去看看 注册个admin账户&#xff0c;登陆进去 一个个点开看&#xff0c;没发现flag 我们也可以由此得出结论&#xff0c;页面存在二次注入的漏洞&#xff0c;并可以大胆猜测修改密码的源代码 resoponse为invalid string的关键字是被过滤的关键字&#xff0c;Le…