数据结构-难点突破(C++/Java详解实现串匹配算法KMP,next数组求法,KMP算法优化nextval数组)

news/2024/4/28 15:32:28/文章来源:https://blog.csdn.net/dodamce/article/details/127674639

文章目录

  • 1. 暴力匹配算法BF
  • 2. KMP算法
    • next数组求法
    • Java代码:
    • C++代码:
    • KMP算法优化nextval数组

1. 暴力匹配算法BF

在了解KMP算法前,就必须介绍串的暴力匹配算法(BF算法)

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

在这里插入图片描述

如果字符匹配,i和j同时向前移动,同时还需要一个变量记录i的初始位置pos。

如果j走到了字符末尾,说明字符串匹配,返回pos即可

否则说明字符串不匹配,j退回原字符串的起始,i变成pos的下一个位置,并且更新pos的值

很容易看出,暴力匹配的算法时间复杂度为O(N2)

C++代码如下:

#include <string>#include <iostream>using namespace std;/*** @brief 字符串暴力匹配算法** @param src 源字符串* @param dst 需要匹配的字符串* @return int 返回第一次出现要匹配的子串的位置,下标从0开始*/
int BF(const string &src, const string &dst)
{if (src.size() == 0 || dst.size() == 0){return -1;}int posSrc = 0;int posDst = 0;while (posSrc < src.length() && posDst < dst.length()){if (src[posSrc] == dst[posDst]){posSrc++;posDst++;}else{//回退posSrc = posSrc - posDst + 1;posDst = 0;}}//说明匹配到最后的字符了,返回第一次出现字串的位置if (posDst == dst.length()){return posSrc - posDst;}else{return -1;}
}int main()
{// cout << BF("abcabcabcdabcde", "abcd") << endl;cout << BF("abcdabcabcdabcde", "abcd") << endl;return 0;
}

2. KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。

KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次
数以达到快速匹配的目的。

具体实现就是通过一个next数组实现,这个数组本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)

KMP算法于BF算法最大的不同点是:
匹配失败后主串的 i 并不会回退,j也不会直接回到0号位置

eg:
首先如果两个串字符匹配的话就同步向前走
在这里插入图片描述
BF算法i就直接回退到1位置,就回退到0位置。实际上并不需要,因为这次一定不可能匹配。

i不回退,这里的最优回退是j回退到2位置。 KMP算法关键就是找j回退到那里。

因为KMP算法本身要求i不会退。所以,需要尽量在已经匹配的主串中找到和字串匹配的部分。

j回退的位置是需要查询next数组的。
next数组定义:保存字串某个位置匹配失败后回退的位置。
next数组的下标就是匹配失败的字符在字串的位置,对应下标的值就是j需要回退的位置。

next数组求法

  1. 找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1 下标字符结尾。
    eg:
    在这里插入图片描述

  2. 找到两个真字串的长度就是这位置匹配失败回退的位置。找不到两个回退位置就是0

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

练习:

下标:0 1 2 3 4 5 6 7 8 9 10 11 12 13a b a b c a b c d a b  c  d  e
next[0]=-1 next[1]=0
next[2]:0下标对应字符为a 1下标对应字符是b  找a开头b结尾的两个真字串next[2]=0(因为2下标前的匹配字符串找不到复合条件的两个真串)
next[3]:0下标对应字符为a ,2下标对应字符是a。找a开头a结尾的两个真字串next[3]=1 (找到的两个真字串长度为1)
next[4]=2;next[5]=0;next[6]=1;next[7]=2;next[8]=0...
next数组为[-1 0 0 1 2 0 1 2 0 0 1 2 0 0]
-----a b c a b c a b c a b c d a b c d e
next数组:
[-1 0 0 0 1 2 3 4 5 6 7 8 9 0 1 2 3 0]

特别注意:找两个相同的最长真字串时,第一个字串必须从0下标开始,第二个字串结尾固定是j-1位置,不能在其他位置找。

如果要实现代码,需要根据next[j]求next[j+1]的值
在这里插入图片描述

Java代码:

public class KMP {public static void InitNext(String dst, int[] next) {next[0] = -1;next[1] = 0;int k = 0;for (int i = 2; i < dst.length(); i++) {// 找str[i-1]==str[k]位置while (k != -1 && dst.charAt(i - 1) != dst.charAt(k)) {k = next[k];}next[i] = k + 1;}}// 使用KMP算法返回src第一个匹配dst的位置,下标从0开始,从src的pos位置开始匹配public static int GetSubStrPos(String src, String dst, int pos) {if (src == null || dst == null || src.length() == 0 || dst.length() == 0)return -1;if (pos >= src.length() || pos < 0)return -1;int i = pos;// 遍历src主串int j = 0;// 遍历dst字串int lenSrc = src.length();int lenDst = dst.length();// 计算字串的next数组int[] next = new int[lenDst];InitNext(dst, next);while (i < lenSrc && j < lenDst) {if (j == -1 || src.charAt(i) == dst.charAt(j)) {//j==-1代表第一个字符就匹配失败,i从第二个字符开始匹配,j从0开始i++;j++;} else {// i不回退j = next[j];}}if (j >= lenDst) {// 匹配成功,返回i-jreturn i - j;} else {return -1;}}public static void main(String[] args) {// System.out.println(GetSubStrPos("abcabcabcdabcde", "abcd", 0));System.out.println(GetSubStrPos("abcdabcabcdabcde", "abcd", 1));}
}

C++代码:

#include <string>
#include <iostream>
#include <vector>
#include <assert.h>using namespace std;//使用KMP算法返回src第一个匹配dst的位置,下标从0开始,从src的pos位置开始匹配void InitNext(const string &dst, vector<int> &next)
{next[0] = -1;next[1] = 0;int k = 0;for (int i = 2; i < dst.size(); i++){while (k != -1 && dst[i - 1] != dst[k]){k = next[k];}next[i] = k + 1;}
}int KMP(const string &src, const string &dst, int pos)
{assert(pos >= 0 && pos < src.length() && src.size() > 0 && dst.size() > 0);int i = pos;int j = 0;int srcSize = src.size();int dstSize = dst.size();vector<int> next(dst.size(), -1);InitNext(dst, next);while (i < srcSize && j < dstSize){if (j == -1 || src[i] == dst[j]){i++;j++;}else{j = next[j];}}if (j == dst.size()){return i - j;}else{return -1;}
}int main()
{// cout << KMP("abcabcabcdabcde", "abcd", 0) << endl;// cout << KMP("abcdabcabcdabcde", "abcd", 0) << endl;cout << KMP("abcdabcabcdabcde", "abcd", 1) << endl;return 0;
}

KMP算法优化nextval数组

nextval数组对KMP算法的优化在于:

原KMP算法,求next数组是一个循环,需要一步一步找到str[i]==str[k]的位置。

nextval数组就是对这一步一步找str[i]==str[k]的优化。

nextval数组生成规则:

  1. 先根据next数组生成规则计算值这就是不匹配需要回退的位置。
  2. 如果当前回退的位置,正好是和当前字符一样,那么就写那个字符的nextval值。不一样就写原来的next数组的值。

在这里插入图片描述

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

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

相关文章

大赛征集令|首届“万应杯”低代码应用开发大赛报名开启啦!

探索&#xff0c;寻觅低码边界。 创新&#xff0c;做成未曾有人做过的事。 首届“万应杯”低代码应用开发大赛 报名正式启动啦&#xff01; 万元现金奖杯/证书项目转售收益 丰厚奖励&#xff0c;邀你来战&#xff01; 大赛时间 低码掘金&#xff0c;就在此时&#xff01; …

MySQL高级SQL语句(一)

MySQL高级SQL语句&#xff08;一&#xff09;MySQL高级SQL语句&#xff08;一&#xff09;一、高级SQL语句&#xff08;进阶查询&#xff09;1.1 select1.2 distinct1.3 where1.4 and 、or1.5 in1.6 between1.7 通配符1.8 like1.9 order by二、函数2.1 数学函数2.2 聚合函数2.3…

MSDC 4.3 接口规范(26)

MSDC 4.3 接口规范&#xff08;26&#xff09;7.4 组呼业务管理7.4.1 服务状态7.4.2 启动组呼业务7.4.2.1 接口函数7.4.2.2 先决条件7.4.2.3 说明7.4.2.4 调用流程7.4.2.4.1 启动组呼业务7.4.2.4.2 无法启动服务7.4.3 停止组呼服务7.4.3.1 接口函数7.4.3.2 先决条件7.4.3.3 说明…

SH-SSS丨《端到端音视频说话人日志网络》论文线上分享

SH Symposium Series on Speech (SH SSS 2022) SH SSS 是由语音之家打造的AI语音技术相关的前沿论文成果分享平台。 来自AI语音技术领域的优秀论文作者、专家学者&#xff0c;用最精炼的表达来解读最新的高质量论文。 分享的论文成果来自国内外顶级会议收录的优秀文章、前沿…

系统kafka不消费-topic问题

测试告诉说kafka的topic列表里面新加入了一个topic&#xff0c;然后就不消费数据了&#xff1b; 自己验证了一下&#xff0c;确实这样&#xff0c;如果去掉新的topic&#xff0c;数据就可以正常消费&#xff1b; 然后我查看定义发现&#xff0c;topicA是1个分区&#xff1b; …

段页式内存管理

文章目录分页、分段的优缺点分析段页式管理分段分页段页式管理的逻辑地址结构段页式存储的段表、页表的地址变换分页、分段的优缺点分析 分页管理它的缺点就是不方便按照逻辑块实现信息的共享和保护而分段管理&#xff0c;如果段长过大&#xff0c;为其分配很大的连续空间会很不…

WebDAV之葫芦儿·派盘+纸间书摘

纸间书摘 支持webdav方式连接葫芦儿派盘。 是专为喜欢做读书笔记的小伙伴量身打造的专属书摘app,不仅仅可以从别的app中导入图书,并且还能来帮助你选择性复制可以来轻松的搞定哦 所有功能完全免费,没有广告,不限制识别次数。 多种备份,本地备份和基于WebDAV协议的云端…

python基于PHP+MySQL的药店药品进销存管理系统

随着科技的发展,针对不同疾病的药品越来越多,不同的药品有不同的属性,用法用量等内容,如何让药店和医药公司更好的对药品进行管理,是很多人都在研究的问题,本系统就是在这样的一个基础上开发出来的 PHP药店药品进销存管理系统通过PHp&#xff1a;MySQL进行开发,主要完成了药店基…

狂神说java基础——面向对象编程

面向对象编程(oop) 1、什么是面向对象(00)面向过程:线性思维 面向对象:分类思维​ 本质:以类的方式组织代码,以对象的形式阻止(封装)数据三大特性:封装,继承,多态2、回顾方法的定义 方法的定义修饰符 返回值类型/** 修饰符 返回值类型 方法名(...){* 方法体* re…

Dropzone V4.5.1 for Mac 文件拖拽工具使用教程

简介 Dropzone 是一款Mac上的文件拖拽操作增强工具&#xff0c;这款软件可以让我们把大部分工作都通过拖拽来完成&#xff0c;比如保存文本、发送邮件、FTP上传、打开应用等等&#xff0c;只需要将文件拖拽到菜单栏上的窗口中即可&#xff0c;并且我们完全可以定制化这些操作&a…

移动测试Appium安装

移动测试Appium安装 一、环境搭建 1.Java sdk安装 并配置JAVA_HOME和PATH 2.Android SDK安装 &#xff08;1&#xff09;解压 &#xff08;2&#xff09;配置ANDROID_HOME和PATH 见教程&#xff1a;AndroidSDK下载及安装 Android SDK 下载安装及配置 3.虚拟机安装 这里下载的…

UnityShader34:非真实感水体渲染

一、水体渲染方案 1.1 水体动画 既然是动画&#xff0c;必然推导公式会和时间相关联&#xff0c;如果不追求表现&#xff0c;可以使用最无脑的 sin 函数&#xff1a; 其中 y 值 振幅*sin(频率*(x值-相对偏移))&#xff0c;感觉目前手机端非真实感渲染的话感觉这一套就够了&a…

Centos下部署CodiMD

Centos下部署CodiMD安装docker安装docker-compose安装git部署CodiMDCodiMD是HackMD的自由软件版本&#xff0c;由HackMD团队开发并开源&#xff0c;具有简化功能&#xff08;无需书本模式&#xff09;&#xff0c;您可以在社区中使用CodiMD&#xff0c;并拥有所有数据。支持浏览…

数据可视化之对外经济发展,近五年我国对外货物进出口总额持续上涨

哈喽&#xff0c;大家好&#xff0c;2021年在疫情仍在冲击全球经济之际&#xff0c;我国不论是在贸易规模方面&#xff0c;还是在国际市场份额方面皆取得进展。 下面是小编对国家统计局最新发布的报告进行报表数据处理分析后得到的数据可视化图表&#xff0c;展示了2021年我国对…

齐活了,Grafana 发布大规模持续性能分析开源数据库 - Phlare

Grafana Phlare 是一个用于聚合 continuous profiling(持续分析)数据的开源软件项目。Grafana Phlare 可以和 Grafana 完全集成&#xff0c;允许你与其他可观察信号相关联。 什么是 continuous profiling? 这个概念很有价值&#xff1a;Profiling 可以帮助你了解程序的资源使…

正规现货黄金中的MACD技术

MACD是整个现货黄金交易平台上面最受投资者欢迎的技术指标&#xff0c;所以我们这次来谈谈&#xff0c;这个全球使用率最高的技术分析指标。 MACD 的全名为 Moving Average Convergence / Divergence &#xff0c;它是一种移动平均线的波动指标&#xff0c;不过它使用的不是普通…

mysql数据库中的插入数据insert,中文字符集配置

目录 关键字insert 常见错误类型 指定一列插入数据 多列同时插入 插入效率问题 全列查询select * 查看数据库字符集类型&#xff1a; 更改数据库字符集 C&#xff1a;create 新增D&#xff1a;update 修改R&#xff1a;retrieve 查询D&#xff1a;delete 删除进行增删查…

Redis客户端RedisTemplate入门学习

Redis的Java客户端 Jedis客户端入门 1.引入依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version></dependency>2.建立连接并操作 //建立连接BeforeEachvoid setUp()…

TIDB 性能测试(TIUP-TPCC)

New-Order&#xff1a;客户输入一笔新的订货交易&#xff1b; Payment: 更新客户账户余额以反映其支付状况; Delivery: 发货(模拟批处理交易); Order-Status: 查询客户最近交易的状态&#xff1b; Stock-Level: 查询仓库库存状况&#xff0c;以便能够及时补货。…

Android Studio入门之常用布局的讲解以及实战(附源码 超详细必看)(包括线性布局、权重布局、相对布局、网格布局、滚动视图 )

运行有问题或需要源码请点赞关注收藏后评论区留言 线性布局LinearLayout 顾名思义&#xff0c;线性布局像是用一根线把它的内部视图串起来&#xff0c;故而内部视图之间的排列顺序是固定的&#xff0c;要么从左到右&#xff0c;要么从上到下排列。通过属性android:orientation…