数据结构--串的模式匹配算法

文章目录

  • 串的模式匹配算法
    • 1.朴素算法(Brute-Force(BF)暴力算法)
      • BF算法分析
    • 2.KMP算法
      • 字符串的最长公共前后缀
      • 部分匹配表(前缀表)Next

串的模式匹配算法

查找子串(模式串)在主串中的位置的操作通常称为串的模式匹配

1.朴素算法(Brute-Force(BF)暴力算法)

如果两个指针(i,j)指向的元素相同则指针后移,不相同则需要回退指针(主串指针回退到上次匹配首位的下一个位置,子串指针回退到开头位置),重复进行上述操作直到主串指针指向主串末尾,即如下所示:

在这里插入图片描述

在2、3、4步骤中,主串的首字符与子串的首字符均不等。在步骤1中主串与子串的前三个字符相等,这就意味着子串的首字符"g"不可能与主串的二、三位相等,故上图中步骤2、3完全是多余的。

也就是说,如果我们知道子串的首字符"g"与后面两个字符不相等(此处需要进行一些预处理),我们就不需要再进行2、3步操作,只保留1、4、5步即可。

在使用朴素算法进行匹配时,主串指针需要进行一些回退。而在知道了子串的一些性质后,主串指针不需要再进行回退,只需一直往前走就行,这样就节省了一些时间开销。

BF算法分析

1.算法在字符比较不相等,需要回溯即i = i - j + 1:即回退到s中的下一个字符开始进行字符匹配。
2.最好情况下的时间复杂度为O(m+n)
3.最坏情况下的时间复杂度为O(m*n)

2.KMP算法

Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP 算法”,常用于在一个文本串 S 内查找一个模式串 T 的出现位置,为了避免朴素算法的低效,由Donald Knuth、Vaughan Pratt、James H. Morris 三人于 1977 年联合发表,故取这 3 人的姓氏命名此算法。

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

字符串的最长公共前后缀

字符串 “ABABA” :

前缀的概念:指的是字符串的子串中从原串最前面开始的子串

前缀有:A,AB,ABA,ABAB

后缀的概念:指的是字符串的子串中在原串结尾处结尾的子串

后缀有:BABA,ABA,BA,A

公共前后缀:一个字符串的所有前缀连续子串和所有后缀连续子串中相等的子串

共前后缀有:A ,ABA

最长公共前后缀:所有公共前后缀的长度最长的那个子串

最长公共前后缀是: ABA

在这里插入图片描述

在得知了子串中有相等的前后缀之后,匹配失败时子串指针不需要回退到开头处,而是回退到相等前缀的后一个位置。

在这里插入图片描述

部分匹配表(前缀表)Next

从第一个字符开始的每个子串的最后一个字符该子串的最长公共前后缀的长度的对应关系表格

其实就是:每个子串的最大相等前后缀的长度

字符串T=“aabaaf "

子串 “a”:最后一个字符是 a,该子串没有前缀也没有后缀,最长公共前后缀长度是 0,因此对应关系就是 a - 0
子串 “aa”:最后一个字符是 a,该子串的最长公共前后缀长度是 1,因此对应关系就是 a- 1
子串 “aab”:最后一个字符是 b,该子串的最长公共前后缀长度是 0,因此对应关系就是 b- 0
子串 “aaba”:最后一个字符是 a,该子串的最长公共前后缀长度是 1,因此对应关系就是 a- 1
子串 “aabaa”:最后一个字符是 a,该子串的最长公共前后缀长度是 2,因此对应关系就是 a- 2
子串 “aabaaf”:最后一个字符是 f,该子串的最长公共前后缀长度是 0,因此对应关系就是 f- 0

所以我们能得到字符串T的前缀表为:

j012345
Taabaaf
next010120

作用:决定了子串指针j在匹配失败时回溯到的位置

在这里插入图片描述

KMP算法就可以整体上分为两步:

1.计算前缀表

import java.util.Arrays;
/*** 计算前缀表next*/
public class Next {/*** 获取一个字符串 pattern 的部分匹配表** @param patternStr 用于模式匹配字符串* @return 存储部分匹配表的每个子串的最长公共前后缀的 next数组*/public int[] kmpNext(String patternStr) {//将 patternStr 转为 字符数组形式char[] patternArr = patternStr.toCharArray();//预先创建一个next数组,用于存储部分匹配表的每个子串的最长公共前后缀int[] next = new int[patternStr.length()];/*从第一个字符(对应索引为0)开始的子串,如果子串的长度为1,那么肯定最长公共前后缀为0因为这唯一的一个字符既是第一个字符,又是最后一个字符,所以前后缀都不存在 -> 最长公共前后缀为0*/next[0] = 0;/*len有两个作用:1. 用于记录当前子串的最长公共前后缀长度2. 同时知道当前子串的最长公共前后缀的前缀字符串对应索引==>子串指针j在匹配失败时回溯到的位置*/int len = 0;//从第二个字符开始遍历,求索引在 [0,i] 的子串的最长公共前后缀长度int i = 1;while (i < patternArr.length) {//比较一下 patternArr[len] 与 patternArr[i] 是否相等if (patternArr[len] == patternArr[i]) {/*1.如果相等即 patternArr[len]==patternArr[i],那么就可以确定存在当前子串的最长公共前后缀2.由于是拼接操作,那么当前子串的最长公共前后缀长度只需要在上一个子串的最长公共前后缀长度的基础上 +1 即可即 next[i] = next[i-1] + 1 3.由于 len 是记录的子串的最长公共前后缀长度, len 还是记录的上一个子串的最长公共前后缀长度,因此:next[i] = next[i-1] + 1 等价于 next[i] = ++len*/next[i] = ++len;//判断以下一个字符结尾的子串的最长公共前后缀长度i++;//  A  B  C  D  A  B  D// [0, 0, 0, 0, 1, 2, 0]}else {/*1.如果不相等 patternArr[len]!=patternArr[i]2.可以先修改len的值:len = next[len-1],再去判断下一个字符是否相等,即 判断 patternArr[len] 是否等于 patternArr[i]3.但实际上我们在这里改为 len = next[len-1] 表示上一个子串的最长公共前后缀字符串的长度*/if(len==0) {/*len为 0说明上一个子串已经没有了公共前后缀字符串则我们没有继续寻找的必要,当前子串的最长公共前后缀字符串长度就是0*/next[i] = len;//继续寻找下一个字符串的最长公共前后缀字符串长度i++;}else{len = next[len - 1];}}}return next;}
}         
//测试
public static void main(String[] args) {Next next = new Next();String patternStr = "ABCDABD";System.out.println(Arrays.toString(next.kmpNext(patternStr)));//输出结果:[0, 0, 0, 0, 1, 2, 0]
}

2.根据前缀表移动两个指针进行匹配

/**  匹配成功一个就退出匹配* @param matchStr   原字符串* @param patternStr 子串* @param next       子串对应的部分匹配表* @return 如果是-1,就是没有匹配到,否则就返回第一个匹配的位置*/
public  int search(String matchStr, String patternStr, int[] next) {int i = 0, j = 0;while (i < matchStr.length() && j < patternStr.length()) {// matchStr = "AABABADDABAC";// patternStr = "BAB";if (matchStr.charAt(i) == patternStr.charAt(j)) {//相等就继续进行匹配i++;j++;} else {//如果 patternStr[i] 和 matchStr[j] 不相等if (j == 0) {/*表示 matchStr 没有匹配到 patternStr的第一个字符那直接将 matchStr 的指针 i 向后移动一位即可*/i++;} else {j = next[j - 1];}}}return j == patternStr.length() ? i - j : -1;
}public static void main(String[] args) {KmpSearch kmpSearch = new KmpSearch();Next next = new Next();String matchStr = "AABABADDABAC";String patternStr = "BAB";int index = kmpSearch.search(matchStr, patternStr, next.kmpNext(patternStr));System.out.println("index = " + index);// 输出:index = 2
}

允许匹配多个,可重复索引字符的代码:

import java.util.ArrayList;/*** kmp搜索算法* 允许匹配多个,可重复索引字符的代码*/
public class Search {/*** @param matchStr   原字符串* @param patternStr 子串* @param next       子串对应的部分匹配表* @return 每次匹配成功的字符串的开始索引位置的集合*/public  ArrayList<Integer> kmpSearch(String matchStr, String patternStr, int[] next) {int i = 0, j = 0;ArrayList<Integer> firstIndexList = new ArrayList<>();while (i < matchStr.length()) {if (matchStr.charAt(i) == patternStr.charAt(j)) {//相等就继续进行匹配i++;j++;} else {//如果 patternStr[i] 和 matchStr[j] 不相等if (j == 0) {/*表示 matchStr 没有匹配到 patternStr的第一个字符那直接将 matchStr 的指针 i 向后移动一位即可*/i++;} else {j = next[j - 1];}}if (j == patternStr.length()) {//超出了最大索引值firstIndexList.add(i - j);j = next[j - 1];}}return firstIndexList;}public static void main(String[] args) {Next next = new Next();Search search = new Search();//原字符串String matchStr = "AABABADDABAC";//子串String patternStr = "ABA";ArrayList<Integer> arrayList = search.kmpSearch(matchStr, patternStr, next.kmpNext(patternStr));System.out.println(arrayList);// 输出:[1, 3, 8]}
}

算法动画页面效果可免费给哦!

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

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

相关文章

《OpenCV计算机视觉》—— 图像形态学(腐蚀、膨胀等)

文章目录 一、图像形态学基本概念二、基本运算1.简单介绍2.代码实现 三、高级运算1.简单介绍2.代码实现 一、图像形态学基本概念 图像形态学是图像处理科学的一个独立分支&#xff0c;它基于集合论和数学形态学的理论&#xff0c;专门用于分析和处理图像中的形状和结构。图像形…

基于YOLOv10的垃圾检测系统

基于YOLOv10的垃圾检测系统 (价格90) 包含 [CardBoard, Glass, Metal, Paper, Plastic] 5个类 [纸板, 玻璃, 金属, 纸张, 塑料] 通过PYQT构建UI界面&#xff0c;包含图片检测&#xff0c;视频检测&#xff0c;摄像头实时检测。 &#xff08;该系统可以根据数据训练出的…

Spring之Bean的生命周期 2024-9-6 19:47

目录 什么是Bean的生命周期为什么要知道Bean的生命周期Bean的生命周期之5步Bean生命周期之7步Bean生命周期之10步 声明&#xff1a;本章博客内容采自老杜2022spring6 语雀文档 什么是Bean的生命周期 Spring其实就是一个管理Bean对象的工厂。它负责对象的创建&#xff0c;对象的…

webpack+lite-server 构建项目示例

首先安装以下库 npm install --save-dev webpack webpack-cli lite-server npm install --save-dev babel-loader babel/core babel/preset-env项目结构 webpack.config.js 配置 const path require("path");module.exports {entry: "./src/index.js",…

数据分析-12-多个时间序列数据的时间戳对齐以及不同的方式补点

参考python时间序列数据的对齐和数据库的分批查询 1 问题场景与分析 1.1 场景 在医院的ICU里,须要持续观察病人的各项生命指标。这些指标的采集频率每每是不一样的(例如有些指标隔几秒采集一个,有些几个小时采集一个,有些一天采集一个),并且有些是按期的,有些是不按期的…

SenseGlove机器臂遥操作控制:技术优势与高危作业安全保障

在追求高效与安全的工业时代&#xff0c;高危作业任务始终是行业发展的一大障碍。SenseGlove力反馈手套机器臂遥操作应用案例的出现&#xff0c;凭借其独特的技术优势&#xff0c;为解决这一难题提供了创新性解决方案。 一、技术优势 高精度的力反馈技术&#xff1a;SenseGlove…

传统CV算法——特征匹配算法

Brute-Force蛮力匹配 Brute-Force蛮力匹配是一种简单直接的模式识别方法&#xff0c;经常用于计算机视觉和数字图像处理领域中的特征匹配。该方法通过逐一比较目标图像中的所有特征点与源图像中的特征点来寻找最佳匹配。这种方法的主要步骤包括&#xff1a; 特征提取&#xff…

设计模式之装饰器模式:让对象功能扩展更优雅的艺术

一、什么是装饰器模式 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff08;Structural Pattern&#xff09;&#xff0c;它允许用户通过一种灵活的方式来动态地给一个对象添加一些额外的职责。就增加功能来说&#xff0c;装饰器模式相比使用…

使用html+css+layui实现动态表格组件

1、概述 需求&#xff0c;表格第一列指标可配置通过后端api传进来&#xff0c;表格显示数据以及鼠标触摸后气泡弹出层提示信息都是从后端传过来&#xff0c;实现动态表格的组件&#xff01;&#xff01;实现效果如下&#xff1a; 接口标准数据格式如下&#xff1a; {"da…

自动驾驶---什么是Frenet坐标系?

1 背景 为什么提出Frenet坐标系&#xff1f;Frenet坐标系的提出主要是为了解决自动驾驶系统在路径规划的问题&#xff0c;它基于以下几个原因&#xff1a; 符合人类的驾驶习惯&#xff1a; 人类驾驶员在驾驶过程中&#xff0c;通常不会关心自己距离起点的横向和纵向距离&#x…

TypeError:未绑定方法

TypeError: unbound method 错误通常发生在类方法被调用时&#xff0c;但没有正确绑定到实例。这通常意味着你试图在类本身上调用一个实例方法&#xff0c;或者没有使用正确的方式创建类实例。 1、问题背景 某位开发者在尝试创建一个类似于经典的 Pratt 递归下降解析器时遇到了…

目前国内外AI,尤其大模型发展的一些新进展

目前&#xff0c;国内外在AI大模型发展方面均取得了一系列的新进展。以下是一些关键点和发展路径的对比&#xff1a; 国际进展 技术创新与应用&#xff1a;国际上的大模型通常由大型科技公司如谷歌、微软、Meta等主导&#xff0c;它们利用现有的大模型技术来增强原有的产品和…

vue3+ts封装类似于微信消息的组件

组件代码如下&#xff1a; <template><div:class"[voice-message, { sent: isSent, received: !isSent }]":style"{ backgroundColor: backgroundColor }"click"togglePlayback"><!-- isSent为false在左侧&#xff0c;为true在右…

Google play最新政策更新和重要提醒

我们都知道&#xff0c;谷歌会定期更新其政策&#xff0c;而政策的变更通常对开发者及其团队的要求会更为严格&#xff0c;也会增加应用上架的一些限制条件&#xff0c;以此提高应用在谷歌商店的质量。 一起来看看Google play最近的一些政策更新和需要注意的地方。 新政策要求 …

【C++】简单易懂的vector迭代器

一、迭代器的本质 vector的迭代器本质上就是一个被封装的指针。迭代器的用法和指针的用法十分相似&#xff0c;就是一个像指针的假指针&#xff0c;我们不妨把迭代器看作一个伪指针。 二、迭代器的使用 句式&#xff08;可以看到迭代器和指针很像&#xff09;&#xff1a; …

5-2 检测内存容量

1 使用的是bios 中断&#xff0c; 每次进行检测都会返回一块 内容。并且标志上&#xff0c;这块内存是否可用。 接下来是代码&#xff1a; 首先是构建 一个文件夹&#xff0c; 两个文件。 types.h 的内容。 #ifndef TYPES_H #define TYPES_H// 基本整数类型&#xff0c;下面的…

2024国赛数学建模ABC题思路模型

完整的思路模型请查看文末名片 完整的思路模型请查看文末名片 完整的思路模型请查看文末名片

rust 命令行工具rsup管理前端npm依赖

学习了一年的 rust 了&#xff0c;但是不知道用来做些什么&#xff0c;也没能赋能到工作中&#xff0c;现在前端基建都已经开始全面进入 rust 领域了&#xff0c;rust 的前端生态是越来越好。但是自己奈何水平不够&#xff0c;想贡献点什么&#xff0c;无从下手。 遂想自己捣鼓…

23种设计模式之责任链模式

文章目录 23种设计模式之责任链模式主要角色和结构工作原理简单实现 - 学生成绩打印优点责任链 - 缺点责任链 - 应用场景责任链模式在Spring中的使用 23种设计模式之责任链模式 责任链设计模式是一种行为型设计模式&#xff0c;它允许多个对象依次处理一个请求&#xff0c;直到…

基于SpringBoot的外卖点餐系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootJSP 工具&#xff1a;IDEA/Eclipse、Navicat、Maven、Tomcat 系统展示 首页 用户管理界…