Java-KMP字符串匹配算法
给两个字符串s和t,如何很快的知道s是否包含t(即t是否是s的子串)。暴力的方法,我们依次以s每个位置为头,去匹配t。
public int find(String s, String t) {char[] ss = s.toCharArray();char[] tt = t.toCharArray();int i = 0;while (i < ss.length) {int j = 0;//以s的每个位置为头匹配int start = i;while (start < ss.length&& j < tt.length) {//有一个不等直接break,匹配下一个位置开头的串if (ss[start] != tt[j]) {break;}start++;j++;}//如果j来到了t字符串的长度,说明上面的循环第一次匹配上了,返回第一次匹配上的位置下标if (j == tt.length) {return start-tt.length;}//否则,从下一个位置开始匹配i++;}return -1;
}
举例,s=ababeababde,t=ababd。我们看一下暴力解法的过程。
1、i=0,start=0,j=0去循环匹配。start=4,j=4时,不匹配,break。start回退到1,j回退到0位置。
2、i=1,start=1,j=0去循环匹配。不匹配,break。start到2,j回退到0位置。
3、i=2,start=2,j=0去循环匹配。不匹配,start=4,j=2时不匹配,break。start回退到3,j回退到0位置。
4、i=3,start=3,j=0去循环匹配。不匹配,break。start到4,j回退到0位置。
5、i=4,start=4,j=0去循环匹配。不匹配,break。start到5,j回退到0位置。
5、i=5,start=5,j=0去循环匹配。匹配完成。(省略中间过程)。
从上述过程中可以看到,已经遍历过的位置会多次回退,无疑增加了时间复杂度。KMP算法过程和暴力遍历时相似的,只是在过程中对回退操作进行了优化,减少了回退。
了解KMP算法前,我们先了解一个定义:最长前后缀相等的长度(前缀和后缀相等的最大长度),对于字符串t,我们生成一个next数组,数组每个位置表示该位置之前的字符串中前缀和后缀相等的最大长度(规定:不能取到字符串最大长度,如果该位置前面没有字符串,最长前后缀相等的长度为-1(人为规定))。
比如上述字符串t=ababd,其next数组为 int[] next=new int[]{-1,0,0,1,2}。假设我们已经有了next数组,我们再看一下上面例子的匹配过程怎么加速?(后续会将next数组如何生成)
1、以0位置开头去匹配,当s来到start=4位置的e时,t位置来到j=4位置的d时,发现不匹配,我们检查next数组,发现4位置的最长前后缀相等的长度(next[4])是2,那么t位置回退到j=next[4]=2,继续开始比较;【为什么可以回退到这进行比较呢?因为s的前4个字符 abab,t的前4个字符 abab 已经比较过了,是相等的,而j=4时next[4]告诉我们当前位置前面字符串的最长前后缀相等长度是2,也就是说t[1]=s[start-1],t[0]=s[start-2],所以这两个位置就不需要再比较了,start就不需要进行回退了,j位置也可以只回退到next[4]位置。】其实此时的含义是,看以start-2位置开头的字符串是否可以匹配出t。
2、start=4,j=2位置开始比较,发现不匹配,此时j=2位置的最长前后缀相等的长度(next[2])是0,所以t的位置回退到j=next[2]=0;
3、start=4,j=0位置开始比较,发现不匹配,此时j已经为0,j无法再回退,所以satrt++;
4、start=5,j=0位置开始比较,匹配完成(后续过程省略)。
从上述过程我们可以看出,s的遍历位置没有回退。下面我们看一下KMP的代码。
public int KMP(String s,String t){if(s==null||n==null||s.length()<t.length()){return -1;}char[] ss=s.toCharArray();char[] tt=t.toCharArray();int N=tt.length;//获取t字符串的next数组 next[i]表示t字符串【i位置以前的最长前后缀相等长度】int[] next=getNextArray(t);int i=0;int j=0;while(i<ss.length){if(ss[i]==tt[j]){i++;j++;}else if(j>0){//t的位置可以回退,回退到当前位置的最长前后缀长度的下一个位置进行比较j=next[j];}else{//j=0&&ss[i]==tt[j]i++;}}return j==tt.length?i-j:-1;
}
关键问题来了,我们如何得到next数组呢?根据规定next[0]=-1,next[1]=0,next[2]=tt[0]==tt[1]?1:0。那么我们考虑一个普遍位置i(即考虑t字符串0-i-1位置字符串的前缀后缀相等情况),假设next[i-1]位置已经计算好了。如下图所示。那么就有以下几种情况:

1、tt[i-1]=tt[next[i-1]](tt[next[i-1]]即左侧的三角位置,next[i-1]标识的两段根据其定义知道相等),所以此时可得出 next[i]=next[i-1]+1;
2、tt[i-1]!=tt[next[i-1]],那如何寻找下一个比较位置呢?我们知道▲位置的next[▲]是该位置的最长前后缀相等长度,那么下一个比较位置就是★标识的位置,而★=next[▲]。以此类推,直到来到0位置,如果还不相等,那就代表i位置以前字符串的最长前后缀相等长度为0。

如此下去,我们得到字符串t的next数组。(最长前后缀相等长度数组)。下面请看代码。
public int[] getNextArray(String t){if (t.length() == 1) {return new int[]{-1};}if (t.length() == 2) {return new int[]{-1, 0};}char[] tt = t.toCharArray();int[] next = new int[t.length()];//规定值next[0] = -1;next[1] = 0;if (tt[0] == tt[1]) {next[2] = 1;}//从2位置开始计算int i = 2;int compareIndex=next[i-1];while(i<tt.length){if(tt[i-1]==tt[compareIndex]){//相等,next[i]就计算出来了,i++计算下一个位置,compareIndex++是next[i]的值//也是计算i+1位置时,第一个需要比较的位置,大家多画图就可以理解了next[i++]=compareIndex++;}else if(compareIndex>0){//计算下一个比较位置compareIndex=next[compareIndex];}else{//compareIndex==0&&tt[i-1]!=tt[compareIndex]next[i]=0;//求下一个位置i++;}}return next;
}
到此整个KMP算法就结束了,了解了原理后,代码还是很好写的。
相关文章:
Java-KMP字符串匹配算法
给两个字符串s和t,如何很快的知道s是否包含t(即t是否是s的子串)。暴力的方法,我们依次以s每个位置为头,去匹配t。 public int find(String s, String t) {char[] ss s.toCharArray();char[] tt t.toCharArray();int …...
Vue3使用vue-count-to数字滚动模块报错解决方案
小伙伴们是不是遇到了vue3项目使用vue-count-to出现报错的问题 报错如下: TypeError: Cannot read properties of undefined (reading _c) 这个错误信息具体是说没读取到_c的属性 具体不清楚是什么原因,排查还得去看源码,所以我们来解决&a…...
【高阶数据结构】线段树加乘(维护序列)详细解释乘与加懒标记
文章目录 1.题目[AHOI2009] 维护序列 2.懒标记处理先加后乘的形式1. 先加后乘的操作 先乘后加的形式2. 先乘后加的操作**乘法操作****加法操作** 懒标记的下传 3.代码 1.题目 题目来源:https://www.luogu.com.cn/problem/P2023 [AHOI2009] 维护序列 题目背景 老师交给小可可…...
replaceState和vue的router.replace删除query参数的区别
使用history.replaceState /*** 替换当前的 history state和url* param {(searchParams:URLSearchParams)>any} cb*/ export const replaceUrlSearch (cb) > {// 获取当前 URLconst url new URL(window.location.href)// 获取 URL 的查询参数const searchParams new …...
[USACO14JAN] Ski Course Rating G
题目大意 滑雪场用一个 N ∗ M N*M N∗M 的整数矩阵表示海拔高度,每个整数表示一个范围在 1 0 9 10^9 109 的高度。每个格子都可以滑到相邻的格子,爱好者们将会在雪场种尽情享受。有些格子被指定为起点,每个起点都要进行评级以帮助爱好者选…...
初步认识 Neo4j 图数据库
Neo4j 是一种高性能的图数据库管理系统,基于图论设计,能够高效地存储和查询复杂的关系数据。以下是关于 Neo4j 的详细介绍: 核心特性 数据模型: Neo4j 使用图数据模型,将数据以节点(Node)、关系…...
Qt中容器 QVector、QList、QSet和QMap 性能与用途比较
表格汇总: 容器存储结构随机访问性能插入/删除性能主要用途QVector连续存储的动态数组 O ( 1 ) O(1) O(1)末尾: O ( 1 ) O(1) O(1),中间: O ( n ) O(n) O(n)频繁随机访问,末尾元素的添加/删除QList优化存储࿰…...
ASP.NET Core - 依赖注入(四)
ASP.NET Core - 依赖注入(四) 4. ASP.NET Core默认服务5. 依赖注入配置变形 4. ASP.NET Core默认服务 之前讲了中间件,实际上一个中间件要正常进行工作,通常需要许多的服务配合进行,而中间件中的服务自然也是通过 Ioc…...
数学用语中 up to 的含义
1. 问题 在数学用语中,常见到“up to”这种用法, 但这种用法与我们常规情况下的用法不同,常令人困惑。 2. “等价关系”说明 已知两个数学对象 a 和 b,以及实数域R, • 当 a 和 b是通过 R 关联的࿰…...
Spring Boot + MyBatis-Flex 配置 ProxySQL 的完整指南
✅ Spring Boot MyBatis-Flex 配置 ProxySQL 的完整指南 下面是一个详细的教程,指导您如何在 Spring Boot 项目中使用 MyBatis-Flex 配置 ProxySQL 进行 读写分离 和 主从同步 的数据库访问。 🎯 目标 在 Spring Boot 中连接 ProxySQL。使用 MyBatis-…...
WEB攻防-通用漏洞_XSS跨站_权限维持_捆绑钓鱼_浏览器漏洞
目录 XSS的分类 XSS跨站-后台植入Cookie&表单劫持 【例1】:利用beef或xss平台实时监控Cookie等凭据实现权限维持 【例2】:XSS-Flash钓鱼配合MSF捆绑上线 【例3】:XSS-浏览器网马配合MSF访问上线 XSS的分类 反射型(非持久…...
人工智能任务20-利用LSTM和Attention机制相结合模型在交通流量预测中的应用
大家好,我是微学AI,今天给大家介绍一下人工智能任务20-利用LSTM和Attention机制相结合模型在交通流量预测中的应用。交通流量预测在现代城市交通管理中是至关重要的一环,它对优化交通资源分配以及提升道路通行效率有着不可忽视的意义。在实际…...
Day04-后端Web基础——Maven基础
目录 Maven课程内容1. Maven初识1.1 什么是Maven?1.2 Maven的作用1.2.1 依赖管理1.2.2 项目构建1.2.3 统一项目结构 2. Maven概述2.1 Maven介绍2.2 Maven模型2.2.1 构建生命周期/阶段(Build lifecycle & phases)2.2.2 项目对象模型 (Project Object Model)2.2.3 依赖管理模…...
Hive SQL必刷练习题:留存率问题
首次登录算作当天新增,第二天也登录了算作一日留存。可以理解为,在10月1号登陆了。在10月2号也登陆了,那这个人就可以算是在1号留存 今日留存率 (今日登录且明天也登录的用户数) / 今日登录的总用户数 * 100% 解决思…...
虚拟同步机(VSG)Matlab/Simulink仿真模型
虚拟同步机控制作为原先博文更新的重点内容,我将在原博客的基础上,再结合近几年的研究热点对其内容进行更新。Ps:VSG相关控制方向的simulink仿真模型基本上都搭建出来了,一些重要的控制算法也完成了实验验证。 现在搭建出来的虚拟…...
单头注意力机制(SHSA)详解
定义与原理 单头注意力机制是Transformer模型中的核心组件之一,它通过模拟人类注意力选择的过程,在复杂的输入序列中识别和聚焦关键信息。这种方法不仅提高了模型的性能,还增强了其解释性,使我们能够洞察模型决策的原因。 单头注意力机制的工作流程主要包括以下几个步骤:…...
【漏洞分析】DDOS攻防分析
0x00 UDP攻击实例 2013年12月30日,网游界发生了一起“追杀”事件。事件的主角是PhantmL0rd(这名字一看就是个玩家)和黑客组织DERP Trolling。 PhantomL0rd,人称“鬼王”,本名James Varga,某专业游戏小组的…...
JavaScript动态渲染页面爬取之Splash
Splash是一个 JavaScript渲染服务,是一个含有 HTTP API的轻量级浏览器,它还对接了 Python 中的 Twisted 库和 OT库。利用它,同样可以爬取动态渲染的页面。 功能介绍 利用 Splash,可以实现如下功能: 异步处理多个网页的渲染过程:获取渲染后…...
慧集通(DataLinkX)iPaaS集成平台-系统管理之UI库管理、流程模板
UI库管理 UI库管理分为平台级和自建两种,其中平台级就是慧集通平台自己内置的一些ui库所有客户均可调用,自建则是平台支持使用者自己根据规则自己新增对应的UI库。具体界面如下: 自建UI库新增界面: 注:平台级UI库不支…...
OpenCV相机标定与3D重建(59)用于立体相机标定的函数stereoCalibrate()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 标定立体相机设置。此函数找到两个相机各自的内参以及两个相机之间的外参。 cv::stereoCalibrate 是 OpenCV 中用于立体相机标定的函数。它通过一…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
ubuntu中安装conda的后遗症
缘由: 在编译rk3588的sdk时,遇到编译buildroot失败,提示如下: 提示缺失expect,但是实测相关工具是在的,如下显示: 然后查找借助各个ai工具,重新安装相关的工具,依然无解。 解决&am…...
