数据结构——串的模式匹配算法(BF算法和KMP算法)

news2024/11/15 15:45:42

算法目的:

        确定主串中所含子串(模式串)第一次出现的位置(定位)

算法应用:

        搜索引擎、拼写检查、语言翻译、数据压缩

算法种类:
        BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的)又称暴力破解法

        KMP算法(特点:速度快)

BF算法

        BF算法亦称简单匹配法,采用穷举的思路。

算法的思路是从S的每一个字符开始依次与T的字符进行匹配。

 

 模式串跟主串逐个字符比较,如果第一个字符相同,则i++、j++比较第二个字符,以此类推,当所有字符相同,则匹配成功,如果有一个不同,模式串就从主串的第二个字符开始比较,即

第二轮比较:

又匹配失败,回溯

再次逐个比较

匹配成功,不用再管主串后面的字符了,此时i = 7,j = 5

模式串的位置为i - t.length = 3

Index(S,T,pos)

  • 将主串的第pos个字符和模式串的第一个字符比较,
  • 若相等,继续逐个比较后面字符;
  • 若不等,从主串的下一字符起,重新与模式串的第一个字符比较
  • 否则,匹配失败,返回值0
int Index_BF(SString S, SString T)
{
	int i = 1, j = 1;
	while (i <= S.length && j <= T.length)
	{
		if (S.ch[i] == T.ch[j]) { ++i; ++j; }//主串和子串依次匹配下一个字符
		else { i = i - j + 2; j = 1; }//主串、子串指针回溯重新开始下一次匹配
	}
	if (j >= T.length)return i - T.length;//返回匹配的第一个字符的下标
	else return 0; //模式匹配不成功
}

BF算法时间复杂度:

若n为主串长度,m为子串长度,最坏的结果是

        主串前面n-m个位置都部分匹配到子串的最后一位,即这n-m位个比较了m次

总次数为:(n-m)*m+m = (n-m+1)*m

若m<<n,则算法时间复杂度为O(n*m)

KMP算法

        该算法较BF算法有较大改进,从而使算法效率有了某种程度的提高。

定义一个 next[j] 函数,,表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。

(1)

首先,主串"BBC ABCDAB ABCDABCDABDE"的第一个字符与模式串"ABCDABD"的第一个字符,进行比较。因为 B 与 A 不匹配,所以模式串后移一位。

(2)

因为 B 与 A 又不匹配,模式串再往后移。

(3)

就这样,直到主串有一个字符,与模式串的第一个字符相同为止。

(4)

接着比较主串和模式串的下一个字符,还是相同。

(5)

直到主串有一个字符,与模式串对应的字符不相同为止。

(6

这时,最自然的反应是,将模式串整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

(7)

一个基本事实是,当空格与 D 不匹配时,你其实是已经知道前面六个字符是"ABCDAB"。KMP 算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,而是继续把它向后移,这样就提高了效率。

(8)

i01234567
模式串ABCDABD'\0'
next[i]-10000120

怎么做到这一点呢?可以针对模式串,设置一个跳转数组int next[],这个数组是怎么计算出来的,后面再介绍,这里只要会用就可以了。

(9)

已知空格与 D 不匹配时,前面六个字符"ABCDAB"是匹配的。根据跳转数组可知,不匹配处 D 的 next 值为 2,因此接下来从模式串下标为 2 的位置开始匹配

(10)

因为空格与 C 不匹配,C 处的 next 值为 0,因此接下来模式串从下标为 0 处开始匹配。

(11)

因为空格与 A 不匹配,此处 next 值为 -1,表示模式串的第一个字符就不匹配,那么直接往后移一位。

(12)

逐位比较,直到发现 C 与 D 不匹配。于是,下一步从下标为 2 的地方开始匹配。

(13)

逐位比较,直到模式串的最后一位,发现完全匹配,于是搜索完成。

next 数组是如何求出的

next 数组的求解基于“真前缀”和“真后缀”,即next[i]等于P[0]...P[i - 1]最长的相同真前后缀的长度(请暂时忽视 i 等于 0 时的情况,下面会有解释)。我们依旧以上述的表格为例,为了方便阅读,我复制在下方了。

i01234567
模式串ABCDABD'\0'
next[ i ]-10000120
  1. i = 0,对于模式串的首字符,我们统一为next[0] = -1
  2. i = 1,前面的字符串为A,其最长相同真前后缀长度为 0,即next[1] = 0
  3. i = 2,前面的字符串为AB,其最长相同真前后缀长度为 0,即next[2] = 0
  4. i = 3,前面的字符串为ABC,其最长相同真前后缀长度为 0,即next[3] = 0
  5. i = 4,前面的字符串为ABCD,其最长相同真前后缀长度为 0,即next[4] = 0
  6. i = 5,前面的字符串为ABCDA,其最长相同真前后缀为A,即next[5] = 1
  7. i = 6,前面的字符串为ABCDAB,其最长相同真前后缀为AB,即next[6] = 2
  8. i = 7,前面的字符串为ABCDABD,其最长相同真前后缀长度为 0,即next[7] = 0

那么,为什么根据最长相同真前后缀的长度就可以实现在不匹配情况下的跳转呢?举个代表性的例子:假如i = 6时不匹配,此时我们是知道其位置前的字符串为ABCDAB,仔细观察这个字符串,首尾都有一个AB,既然在i = 6处的 D 不匹配,我们为何不直接把i = 2处的 C 拿过来继续比较呢,因为都有一个AB啊,而这个AB就是ABCDAB的最长相同真前后缀,其长度 2 正好是跳转的下标位置。

有的读者可能存在疑问,若在i = 5时匹配失败,按照我讲解的思路,此时应该把i = 1处的字符拿过来继续比较,但是这两个位置的字符是一样的啊,都是B,既然一样,拿过来比较不就是无用功了么?其实不是我讲解的有问题,也不是这个算法有问题,而是这个算法还未优化,关于这个问题在下面会详细说明,不过建议读者不要在这里纠结,跳过这个,下面你自然会恍然大悟。

思路如此简单,接下来就是代码实现了,如下:


#include <stdio.h>
#include <string.h>
void get_next(char s[],int next[]);
int KMP(char s1[],char s2[],int next[]);
int main() {
	int i= 0;
	int next[1000];
	char s2[] = "ce";
	char s1[] = "ababce";
	get_next(s2,next);
    
	i=KMP(s1,s2,next);
    printf("%d\n",i);
    return 0;
}
void get_next(char s[],int next[])
{	
	int len=0;
    int i=0;//后缀 
    int j=-1;//前缀 
    next[0]=-1;//第一位符前面没有前缀,由公式知设为-1. 
    len=strlen(s);
    while(i<len)  
    {
        if(j==-1||s[i]==s[j])
        {
            i++;
            j++;
			next[i]=j;
        }
        else
        {
            j=next[j];
        }
    }
}
int KMP(char s1[],char s2[],int next[])
{
    int i=-1;
    int j=-1;
    int len1=strlen(s1);
    int len2=strlen(s2);
    while(i<len1&&j<len2)
    {
        if(j==-1||s1[i]==s2[j])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
    }
    if(j>=len2)
        return i-len2+1;
    else
        return 0;
}

参考:

c++ - KMP 算法 - 经典算法与数据结构 - SegmentFault 思否

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

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

相关文章

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解 题目传送门 题解 CSP-S1 补全程序&#xff0c;致敬全 A 的答案&#xff0c;和神奇的预言家。 写一下这篇的题解说不定能加 CSP 2024 的 RP 首先看到 k k k 这么大的一个常数&#xff0c;就想到了二分。然后写一个判…

《深入理解JAVA虚拟机(第2版)》- 第13章 - 学习笔记【终章】

第13章 线程安全与锁优化 13.1 概述 面向过程的编程思想 将数据和过程独立分开&#xff0c;数据是问题空间中的客体&#xff0c;程序代码是用来处理数据的&#xff0c;这种站在计算机角度来抽象和解决问题的思维方式&#xff0c;称为面向对象的编程思想。 面向对象的编程思想…

一劳永逸:用脚本实现夸克网盘内容自动更新

系统环境&#xff1a;debian/ubuntu 、 安装了python3 原作者项目&#xff1a;https://github.com/Cp0204/quark-auto-save 感谢 缘起 我喜欢看电影追剧&#xff0c;会经常转存一些资源到夸克网盘&#xff0c;电影还好&#xff0c;如果是电视剧&#xff0c;麻烦就来了。 对于一…

Kettle的安装及简单使用

Kettle的安装及简单使用一、kettle概述二、kettle安装部署和使用Windows下安装案例1&#xff1a;MySQL to MySQL案例2&#xff1a;使用作业执行上述转换&#xff0c;并且额外在表stu2中添加一条数据案例3&#xff1a;将hive表的数据输出到hdfs案例4&#xff1a;读取hdfs文件并将…

Jboss常⻅中间件漏洞

一.CVE-2015-7501 环境搭建 cd vulhub-master/jboss/JMXInvokerServlet-deserialization docker-compose up -d 1.POC&#xff0c;访问地址 172.16.1.4:8080/invoker/JMXInvokerServlet 返回如下&#xff0c;说明接⼝开放&#xff0c;此接⼝存在反序列化漏洞 2.下载 ysose…

7.C++程序中的基本数据类型-数据类型之间的转换

在C中&#xff0c;类型转换是将一个数据类型转为另外一个数据类型&#xff0c;其转换过程比较复杂&#xff0c;目前只讨论基本数据类型之间的转换。 类型转换分为两部分&#xff1a;隐式转换和显示转换 隐式转换又称为自动转换&#xff0c;显示转换又称为强制转换。 隐式转换…

[Linux] Linux进程PCB内部信息的深入理解

标题&#xff1a;[Linux] Linux进程PCB内部信息的深入理解 个人主页&#xff1a;水墨不写bug &#xff08;图片来自网络&#xff09; 目录 一.查看进程 二.认识并了解进程的关键信息 I&#xff0c;PID/PPID II&#xff0c;exe III&#xff0c;cwd 三、fork&#xff08;&…

vue源码分析(九)—— 合并配置

文章目录 前言1.vue cli 创建一个基本的vue2 项目2.将mian.js文件改成如下3. 运行结果及其疑问&#xff1f; 一、使用 new Vue 创建过程的 2 种场景二、margeOption的详细说明1.margeOption的方法地址2.合并策略的具体使用3.defaultStrat 默认策略方法 三&#xff1a;以生命周期…

OpenResty安装及使用

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

调用本地大模型服务出现PermissionDeniedError的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【机器学习】---神经架构搜索(NAS)

这里写目录标题 引言1. 什么是神经架构搜索&#xff08;NAS&#xff09;1.1 为什么需要NAS&#xff1f; 2. NAS的三大组件2.1 搜索空间搜索空间设计的考虑因素&#xff1a; 2.2 搜索策略2.3 性能估计 3. NAS的主要方法3.1 基于强化学习的NAS3.2 基于进化算法的NAS3.3 基于梯度的…

ICM20948 DMP代码详解(38)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;37&#xff09; 上一回继续解析inv_icm20948_set_slave_compass_id函数&#xff0c;解析了第3段代码&#xff0c;本回解析接下来的代码。为了便于理解和回顾&#xff0c;再次贴出该函数源码&#xff0c;在EMD-Core\so…

队列+宽搜专题篇

目录 N叉树的层序遍历 二叉树的锯齿形层序遍历 二叉树最大宽度 在每个树行中找最大值 N叉树的层序遍历 题目 思路 使用队列层序遍历来解决这道题&#xff0c;首先判断根节点是否为空&#xff0c;为空则返回空的二维数组&#xff1b;否则&#xff0c;就进行层序遍历&#x…

chapter 12 Bandgap References

chapter 12 Bandgap References 这一章我们介绍电压和电流基准. 首先学习和输入电压无关的偏置和起动问题. 然后介绍和温度有关的基准电压, 考虑offset对输出电压的影响. 最后学习恒定gm偏置和state-of-the-art电压基准. 12.1 General Considerations 一般基准电流有三种形式…

鸿蒙OpenHarmony【轻量系统芯片移植案例】标准系统方案之瑞芯微RK3566移植案例

标准系统方案之瑞芯微RK3566移植案例 本文章是基于瑞芯微RK3566芯片的khdvk_3566b开发板&#xff0c;进行标准系统相关功能的移植&#xff0c;主要包括产品配置添加&#xff0c;内核启动、升级&#xff0c;音频ADM化&#xff0c;Camera&#xff0c;TP&#xff0c;LCD&#xff…

数据集-目标检测系列-摩托车检测数据集 motorcycle >> DataBall

数据集-目标检测系列-摩托车检测数据集 motorcycle >> DataBall 数据集-目标检测系列-摩托车&#xff08;motorcycle&#xff09;检测数据集 数据量&#xff1a;1W 想要进一步了解&#xff0c;请联系 DataBall。 DataBall 助力快速掌握数据集的信息和使用方式&#xf…

AI 智能名片链动 2+1 模式商城小程序中的体验策略

摘要&#xff1a;本文探讨了在 AI 智能名片链动 21 模式商城小程序中&#xff0c;体验策略如何服务于用户体验&#xff0c;以及与产品策略的区别。重点分析了该小程序如何通过关注用户在使用过程中的流畅度、视觉体感等方面&#xff0c;实现“让用户用得爽”的目标&#xff0c;…

MWD天气图像多分类数据集,用于图像分类总共6个类别,多云,下雨,下雪,雾天,正常天气,共60000张图像数据

MWD天气图像多分类数据集&#xff0c;用于图像分类 总共6个类别&#xff0c;多云&#xff0c;下雨&#xff0c;下雪&#xff0c;雾天&#xff0c;正常天气&#xff0c;共60000张图像数据 MWD天气图像多分类数据集 (Multi-Weather Dataset, MWD) 数据集描述 MWD天气图像多分类…

哔哩哔哩自动批量删除抽奖动态解析篇(三)

前面两节已经介绍了如何获取抽奖动态列表和根据动态id_str索引值判断是否开奖了&#xff0c;接下来我们要完成的就是删除已经开奖了的动态 一、删除已经开奖的抽奖动态 老规矩&#xff0c;我们按F12健进入网页源代码&#xff0c;删除一条动态&#xff0c;观察网络接口 这次…

论文阅读 | 可证安全隐写(网络空间安全科学学报 2023)

可证安全隐写&#xff1a;理论、应用与展望 一、什么是可证安全隐写&#xff1f; 对于经验安全的隐写算法&#xff0c;即使其算法设计得相当周密&#xff0c;隐写分析者&#xff08;攻击者&#xff09;在观察了足够数量的载密&#xff08;含有隐写信息的数据&#xff09;和载体…