字符串匹配算法--KMP算法--BM算法
该算法解决的是字符串匹配问题,即查看字符串中是否含有完整的匹配字符串。如在java的string的contains方法
匹配问题最简单的就是暴力破解了。在java的contains也是这么实现的,效率是低一点的。如果想要更快的速度可以自己写KMP算法。
代码实现体验
Knuth-Morris-Pratt
KMP算法也不是特别高级的一种,只是对暴力法的一种优化,节省了很多不必要的匹配过程。
如
假定:
文本为A子串
匹配文本为B子串
这里是相同的
如果假定2号是匹配上的那么画线部分应该需要匹配上
3号同理
这里可以看出来
如果匹配成功A后缀对应的会是匹配成功B前缀。
所以我们如果发现A后缀和B前缀相同就可以将B移动到那个位置。
我们会发现A后缀和B前缀都会是B的一部分(因为匹配成功了)
所以移动的位置只和B有关,我们就可以构建一个前缀表。如果匹配了 i i i 个字符那么就移动 n e x t [ i ] next[i] next[i] 位。
所以如何获取next表呢
用双指针
为什么呢:
这里利用的应该算动态规划的思想。
我们首先看next代表的是什么:
next代表对于前面部分匹配成功而最后一个匹配识别而指针指向的方向
那么我们可以知道上面的这个情况就等同于文本为AAAB匹配文为AAA的情况了。那么这个时候我们就需要将匹配文指针指向next[]
protected static int[] getNext(String pattern) {// 初始化next数组和指针int[] next = new int[pattern.length()];next[0] = -1;// 后缀指针int i = 0;// 前缀指针int j = -1;// 生成next数组while (i < pattern.length() - 1) {// j == -1 代表j已经到了开头if (j == -1 || pattern.charAt(i) == pattern.charAt(j)) {i++;j++;next[i] = j;} else {j = next[j];}}return next;}
因为这个生成匹配next数组和这个的思想是一样的。这里直接给出代码了
private static int KMPMatch(String text,String pattern,int[] next){int i = 0;int j = 0;while (i < text.length() && j < pattern.length()) {if (j == -1 || text.charAt(i) == pattern.charAt(j)) {i++;j++;} else {j = next[j];}}if (j == pattern.length())return i - j;elsereturn -1;}
Boyer-Moore
KMP算法是从左到右比较,而BM算法从右到左比较。而BM的这种做法也使得算法变得更简单了。
简化版本Horspool算法
就是从最后一个开始匹配如果没有匹配成功,则将离末尾最近的一个拿过来匹配。
如
那么就将最近的A移动过来,然后继续从最后一位开始匹配
这样可以大大的减少匹配的次数。
为了加快移动的速度,我们可以用一个匹配表来加速。
如BARBER
A | B | E | R | 其他 |
---|---|---|---|---|
4 | 2 | 1 | 3 | 6 |
其代表的是
如果该字符串当前不匹配,那么字符串向右移动的距离。
代码
这里是用map实现的,也可以用数组实现。在最开始的运行代码里面有。
private static Map<Character, Integer> getTable(String pattern) {HashMap<Character, Integer> table = new HashMap<>();for (int i = 0; i < pattern.length() - 1; i++)table.put(pattern.charAt(i), pattern.length() - 1 - i);return table;}public static int horspool(String text, String pattern) {Map<Character, Integer> table = getTable(pattern);int offset = 0;while (offset <= text.length() - pattern.length()) {int i = pattern.length() - 1;while (i >= 0 && pattern.charAt(i) == text.charAt(offset + i)) i--;if (i < 0)return offset;else offset += table.getOrDefault(text.charAt(offset + pattern.length() - 1), pattern.length());}return -1;}
Boyer-Moore算法
BM算法是是在horspool算法的进一步优化。
然而在遇到第一个不匹配字符之前已经有k个字符匹配成功了,那么这2个算法的操作是不同的。
BM算法定义了两个规则:
- 坏字符规则:当文本串中的某个字符跟模式串的某个字符不匹配时,我们称文本串中的这个失配字符为坏字符,此时:
移动位数 d 1 = m a x { t 1 ( 失配字符 ) − 已经匹配字符数量 , 1 } 移动位数d_1=max\{t_1(失配字符)-已经匹配字符数量,1\} 移动位数d1=max{t1(失配字符)−已经匹配字符数量,1}
t 1 的表和 h o r s p o l l 的是一样的 t_1的表和horspoll的是一样的 t1的表和horspoll的是一样的 - 好后缀规则:当字符失配时,已经匹配上的我们称为好后缀。
移动位数 d 2 = 后缀移动表 t 2 ( 匹配字符数量 ) 移动位数d_2=后缀移动表t_2(匹配字符数量) 移动位数d2=后缀移动表t2(匹配字符数量) - 总的移动 = m a x { d 1 , d 2 } = max\{d_1,d_2\} =max{d1,d2}
坏字符我们已经学过了。
我们来看看好后缀是如何建表的。
好后缀就是相当于,在匹配文本中寻找本次已经匹配的后缀是否含有。
如下:
这个原理应该很好理解但是,代码怎么来实现呢。
getTable(pattern);就是上面的代码。
public static int match(String text, String pattern) {buildSuffixTable(pattern);getTable(pattern);int offset = 0;int l = pattern.length();while (offset <= text.length() - l) {int i = l - 1;while (i >= 0 && pattern.charAt(i) == text.charAt(offset + i)) {i--;}if (i < 0) {
// System.out.println(offset + 1);
// offset++;return offset;} else if (i == l - 1) {offset += table.getOrDefault(text.charAt(offset + i), l);} else {int d1 = Math.max(1, table.getOrDefault(text.charAt(offset + l - 1), l) - i);offset += Math.max(d1, gs[l - 1 - i]);}}return -1;}private static int[] gs;protected static void buildSuffixTable(String s) {char[] pattern = s.toCharArray();gs = new int[pattern.length]; // 模式串int[] suffix = new int[pattern.length];Arrays.fill(suffix, -1);for (int i = 1; i <= pattern.length - 1; i++) {int j = i - 1;int k = 0;while (j >= 0 && pattern[j] == pattern[pattern.length - 1 - k]) {j--;k++;suffix[k] = j + 1;}}
// System.out.println(Arrays.toString(suffix));int i = 1;while (i < suffix.length && suffix[i] != -1) {gs[i] = pattern.length - i - suffix[i];i++;}int j = i - 1;while (j >= 0) {if (s.substring(s.length() - j).equals(s.substring(0, j))) {Arrays.fill(gs, i, gs.length, gs.length - j);break;}j--;}if (j == -1)Arrays.fill(gs, i, gs.length, gs.length);}
但是效果来说呢没有快哎。
相关文章:

字符串匹配算法--KMP算法--BM算法
该算法解决的是字符串匹配问题,即查看字符串中是否含有完整的匹配字符串。如在java的string的contains方法匹配问题最简单的就是暴力破解了。在java的contains也是这么实现的,效率是低一点的。如果想要更快的速度可以自己写KMP算法。 代码实现体验 Knut…...

swagger的简单介绍
目录 swagger是什么? swagger有什么用? Swagger包含的工具集: swagger的使用步骤: swagger的相关注解: Docket的源码 了解swagger的作用和概念了解前后端分离在SpringBoot中集成Swagger swagger是什么?…...

HNU-电路与电子学-小班3
第三次讨论 1 、直接用晶体管而不是逻辑门实现异或门,并解释这个电路是如何工作的。 (6个 MOS 管构成) 2 、通信双方约定采用 7 位海明码进行数据传输。请为发送方设计海明码校验位 生成电路,采用功能块和逻辑门为接收方设计海…...

[机缘参悟-98] :层次不同、维度不同、视角不同、结论不同
目录 全局VS具备, 总体V部分 认知的六个认知层次: 认知的六个立体化维度: 0、维空间,点思维 1、一维空间,直线思维 2、二维空间,平面思维 3、三维空间:立体思维。 4、四维空间ÿ…...

chatgpt-web发布之docker打包流程
docker打包流程 1、使用docker前置准备: 电脑下载docker桌面版,以及开启虚拟机步骤:https://blog.csdn.net/qq_34905631/article/details/126573826下载docker桌面版 :https://docs.docker.com/desktop/install/windows-install…...

动态优化会议地点
前言 在现在快节奏的工作节奏下,大家的活动范围越来越广,但是出行成本也相应提高。在集体会面的时候,如何选择合适的地点成为了一个棘手的问题。本文将介绍如何通过动态优化选择会议地点,以达到平均交通成本最低的目标。 动态优化…...

Golang每日一练(leetDay0076) 第k大元素、组合总和III
目录 215. 数组中的第K个最大元素 Kth-largest-element-in-an-array 🌟🌟 216. 组合总和 III Combination Sum iii 🌟🌟 🌟 每日一练刷题专栏 🌟 Rust每日一练 专栏 Golang每日一练 专栏 Python每日…...

可节省60% MCU开发成本的NV080D-S8,单片机语音芯片在恒温碗上的应用
社会在不断进步,科技在不断发展,如今的恒温碗不仅带有温度显示功能,更附带有语音播报,能更好地知晓当前饭菜,变凉或过烫的情况,有效避免伤害宝宝脆弱的肠胃; 广州九芯电子推出了一款,…...
Java并发常见面试题
参考:javauide、程序员大斌、面试宝典 1.并发与并行的区别 并发:两个及两个以上的作业在同一 时间段 内执行。并行:两个及两个以上的作业在同一 时刻 执行。2.同步和异步的区别 同步:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。异步:调用在发…...

基于vue3+pinia2仿ChatGPT聊天实例|vite4.x仿chatgpt界面
使用vue3pinia2开发仿制chatgpt界面聊天实例Vue3-Chatgpt 基于Vue3.xPinia2VueRouterVue3-Markdown等技术构建仿ChatGPT网页端聊天程序。支持经典分栏界面布局、light/dark模式、全屏半屏显示、Markdown语法解析、侧边栏隐藏等功能。 技术框架 编辑工具:Cursor框架…...
JDK动态代理和CGLIB动态代理
JDK动态代理和CGLIB动态代理 JDK动态代理和CGLIB动态代理 JDK动态代理和CGLIB动态代理 ① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时&#x…...

Jetpack Hilt 框架的基本使用
什么是 Hilt? Hilt 是一个功能强大、用法简单的依赖注入框架,于 2020 年加入到 Jetpack 家族中。它是 Android 团队联系了 Dagger2 团队,一起开发出来的一个专门面向 Android 的依赖注入框架。相比于 Dagger2,Hilt 最明显的特征就…...
exec()在不同namespace执行结果的区别
记录一个很tricky的问题,下面这段code在执行func1时会出现NameError: name List is not defined,但执行func2时一切正常。 import typescontent """ from typing import Listclass GeneratedData:qna: List"""def func1…...
人工智能革命中的22个隐藏职业:推动科技行业的变革
作者 | Manas Sadangi 随着人工智能技术的不断发展,它正在创造一系列前所未有的就业机会。虽然数据科学家、机器学习工程师和人工智能研究人员等传统的人工智能角色得到了广泛认可,但在推动科技行业变革方面,还有一些鲜为人知的职业同样重要。…...
算法题3 — 求字符串中的最长子串
文章目录 题目示例示例1示例2示例3 解题解法1解法2 leetcode 题目 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 示例1 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例…...

【FreeRTOS】——中断优先级设置中断相关寄存器临界段代码保护调度器挂起与恢复
目录 前言: 一、中断优先级设置 二、中断相关寄存器(STM32-Cortex M3) 三、临界段代码保护 四、任务调度器的挂起和恢复 总结: 前言: 博客笔记根据正点原子视频教程编辑,仅供学习交流使用࿰…...
1.2 什么是eBPF?(下)
四,eBPF的优势 4.1 eBPF程序的动态加载 eBPF程序可以动态地加载到内核中,或从内核中删除。这个要与内核模块的加载与卸载区分开来。这里顺便讨论下eBPF程序与内核模块的区别,如下: 而Linux内核模块是面向内核API编程的,可以直接运行在内核当中。eBPF程序是面向BPF体系结构…...

掌握哪些测试技术才能说自己已经学成了?
一、过硬的基础能力 其实所有的测试大佬都是从底层基础开始的,随着时间,经验的积累慢慢变成大佬。要想稳扎稳打在测试行业深耕,成为测试大牛,首当其冲的肯定就是拥有过硬的基础,所有的基础都是根基,后期所…...
什么是C语言?
C语言是一种高级编程语言,于1972年由Dennis Ritchie在贝尔实验室开发出来。它是一种通用的、结构化的编程语言,被广泛用于系统软件、嵌入式系统、游戏开发以及科学计算等领域。 C语言的设计目标是提供一种简洁、高效、可移植的编程语言,以便…...

SAP-物料主数据-质量管理视图字段解析
过账到质检库存:要勾选,否则收货后库存不进入质检库存HU检验:收货到启用HU管理的库位时产生检验批,例如某个成品物料是收货到C002库位,该库位启用了HU管理,那么此处要勾选。但是如果勾选了,却收…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...