数据结构:利用递推式计算next表
next 表是 KMP 算法的核心内容,下面介绍一种计算 next 表的方法:利用递推式计算
如图 6.3.1 所示,在某一趟匹配中,当对比到最后一个字符的时候,发现匹配失败(s[i] ≠ t[j])。根据 BF 算法,则令 i = i - j + 1; j=0 ,两个字符串的指针同步回退,然后从该位置开始继续比对。

在下一趟匹配过程中,对于模式串 t 而言,还要从头开始,尽管有一些字符在前一趟匹配中已经比对过且成功。如此就造成了时间上的浪费。
以图 6.3.1 为例,s[i-j, i) 完全是由 0 组成的,那么,在 i, j 回退之后的下一趟比对中,前 j − 1 j-1 j−1 次比对必然会成功。因此,可以令 i 不回退,保持不变,令 j = j - 1 。这样,就减少了比对次数。
上述“令 i 保持不变、j = j - 1”的含义,可以理解为:令 t 相对于 s 向右移动一个单元,然后从前一个匹配失败位置继续比对,如图 6.3.2 所示。

将上述案例推广到稍微一般的情况,如图 6.3.3 所示,在某一轮匹配中,s[i]='E' ≠ 'O'=t[4] ,在此为止匹配失败后,由于 s[i-4, i) = t[0, 4) = 'REGR' ,即 s[i] 左侧的若干字符已经匹配,所以,接下来只需将 t[0] 与 s[i-1] 对齐即可,或者说将 t 向右移动 4-1=3 个字符,等效于 i 保持不变,同时令 j = 1 ,然后继续匹配。

将上面的讨论,再向更一般化的情况推广,如图 6.3.4 所示,假设前一趟比对终止于 s[i] ≠ t[j] ,按照上述操作,指针 i 不必回退,而是将 s[i] 与 t[k] 对齐,并进行下一趟比对。
那么,问题是 k 应该如何确定?

结合图 6.3.4 以及前面两个案例,经过前一趟比对,已经确定匹配的范围是:t[0, j) = s[i-j, i) 。
于是,若模式串 t 经适当向右移动之后,能够与 s 的某一子串完全匹配(包含 s[i] 在内),则其中的必要条件是:t[0, k) = s[i-k, i) = t[j-k, j) 。
也就是,在 t[0, j) 中长度为 k 的真前缀 t[0, k) ,应与长度为 k 的真后缀 t[j-k, j] 完全匹配。
空串是任何字符串的子串,也是任何字符串的前缀和后缀; 任何字符串都是自己的子串,也是自己的前缀和后缀。此类子串、前缀和后缀分别称作平凡子串(trivial substring)、平凡前缀(trivial prefix)和平凡后缀(trivial suffix)。
字符串本身之外的所有非空子串、前缀和后缀,分别称作真子串(proper substring)、 真前缀(proper prefix)和真后缀(proper suffix)。
所以,k 必然来自集合:
N ( t , j ) = { 0 ≤ k < j ∣ t [ 0 , k ) = t [ j − k , j ) } N(t, j) = \{0\le k\lt j~|~t[0, k) = t[j-k,j)\} N(t,j)={0≤k<j ∣ t[0,k)=t[j−k,j)}
一般地,该集合可能包含多个符合条件的 k 。但需要特别注意的是,其中具体由哪些 k 值构成,仅取决于模式串 t 以及前一趟比对的首个匹配失败位置 t[j] ,而与主串 s 无关。
从图 6.3.4 还可以看出,若下一轮比对从 s[i] 与 t[k] 的比对开始,这等效于将 t 向右移动 j - k 个单元,位移量与 k 负相关。因此,为保证 t 与 s 的对齐位置不倒退(指针 i 不回退),同时又不至于遗漏任何可能得匹配,应在集合 N ( t , j ) N(t, j) N(t,j) 中挑选最大的 k 。也就是说,当有多个 k 值时,即多个向右移动 t 的方案时,应该保守地选择其中移动距离最短者,即取 k 最大。于是,令:
next[j] = max ( N ( t , j ) ) \text{next[j]} = \max(N(t,j)) next[j]=max(N(t,j))
则一旦发现 t[j] 与 s[i] 匹配失败,即可转而将 t[ next[j] ] 与 s[i] 对齐,并从这一位置开始继续下一趟比对。
既然集合 N ( t , j ) N(t, j) N(t,j) 仅取决于模式串 t 以及匹配失败位置 j,而与主串 s 无关,作为其中的最大元素, next[j] 也必然具有这一性质。于是,对于任一模式串 t ,不妨通过预处理提前计算出所有位置 j 所对应的next[j] 值,并整理为表格(即下面介绍的“next 表”),以便此后反复查询——亦即,将“记忆力”转化为“预知力”。
根据 N ( t , j ) N(t,j) N(t,j) 集合可知,只要 j > 0 j\gt 0 j>0 ,必然有 0 ∈ N ( t , j ) 0\in N(t,j) 0∈N(t,j) 。此时集合 N ( t , j ) N(t,j) N(t,j) 非空,从而可以保证“在其中能够取到最大值”,即 next[j] = max ( N ( t , j ) ) \text{next[j]} = \max(N(t,j)) next[j]=max(N(t,j)) 。
- 令
next[0] = -1
当 j = 0 j=0 j=0 时,即 t 中的第一个字符与 s 中的字符不匹配,集合 N ( t , j ) N(t,j) N(t,j) 为空集,此时,next[j=0] 的值应该是多少?
当某一趟匹配中,如果模式串 t 的第一个字符即匹配失败,根据匹配过程可知,应该将模式串 t 向右移动一个字符,然后启动下一趟匹配。为此,如果假想模式串 t 的首字符 t[0] 的左侧再有一个字符,可以记作 t[-1] ,并且该字符与任何字符都是匹配的。那么,在这趟本来是 t 的首字符匹配失败的操作中,就可以认为 t[-1] 与主串中对应字符匹配成功,于是“将模式串 t 向右移动一个字符”就可以认为是将 t[-1] 与 s[i] 对齐。
经过上述分析,当 j = 0 j=0 j=0 时,可认为 next[0] = -1 。
- 计算
next[j + 1]
前面已经讲过,next[j] 与主串 s 无关,取决于模式串 t 以及匹配失败的位置 j 。于是,“对于任一模式串 t ,不妨通过预处理提前计算出所有位置 j 所对应的next[j] 值,并整理为表格(即下面介绍的“next 表”),以便此后反复查询”。
在确定了 next[0] = -1 之后,后续的 next[j] 可以使用递推方式计算。
假设已知 next[0] 到 next[j] 的值,由此递推出 next[j+1] 的值。
若 next[j] = k ,则意味着在 t[0, j) 中,自匹配的真前缀和真后缀的最大长度为 k ,故必然有:
next[j+1] ≤ next[j] + 1
而且,当且仅当 t[j] = t[k] 时取等号,即 next[j+1] = next[j] + 1 = k + 1 ,如图 6.3.5 所示。

当 t[j] ≠ t[k] 时,则可以不断向前查找,next[next[j]] + 1, next[next[next[j]]] + 1, ... ,直到 t[-1] 这个与任何字符都匹配的字符,在这个过程中,必然会有能够满足 next[j+1] = next[... next[j] ...] + 1 成立的 t[k] (如图 6.3.6 所示)。即:反复用 next[k] 替换 k (令 k = next[k]),一旦发现 t[j] 与 t[k] 匹配(含与 t[k = -1] 的通配),即可令 next[j + 1] = next[k] + 1 .

既然总有 next[k] < k ,故在此过程中 k 必然严格递减;同时,即便 k 降低至 0,亦必然会终止于通配的next[0] = -1,而不致下溢。如此,该算法的正确性完全可以保证。
由此,就可以创建 next 表。
例 6.3.1 已知串 t = '000010' ,计算 next 表(或称:next 数组值)。
【解】
-
设
next[0] = -1。 -
j = 1时,k = next[j-1] = next[0] = -1(此时,前面的 next 数组值已经得到,所以必然有next[j-1] = k,由此可以计算得到k)。比较
t[j-1]和t[k](因为next[j] ≤ next[j-1] + 1,当且仅当t[j-1] = t[k]时取等号):t[j-1] = t[0] = 0,t[k] = t[-1] = *,相等(通配),所以:next[j] = k+1(因为next[j-1] = k,),next[1] = -1+1 = 0。 -
j = 2时,k = next[j-1] = next[1] = 0。因为
t[j-1=1] = 0,t[k=0] = 0,相等,所以next[j=2] = k + 1 = 0 + 1 = 1。 -
j = 3时,k = next[2] = 1。因为
t[j-1=2] = 0,t[k=1] = 0,相等,所以next[j=3] = k + 1 = 1 + 1 = 2。 -
j = 4时,k = next[j-1=3] = 2。因为
t[j-1=3] =0,t[k=2] = 0,相等,所以next[j=4] = k + 1 = 2 + 1 = 3。 -
j = 5时,k = next[j-1=4] = 3。因为
t[j-1=4]=1,t[k=3] = 0,不相等。 则
k = next[k=3] = 2:因为t[j-1=4]=1,t[k=2] = 0,不相等。 则
k = next[k=2] = 1:因为t[j-1=4]=1,t[k=1] = 0,不相等。 则
k = next[k=1] = 0:因为t[j-1=4]=1,t[k=0] = 0,不相等。 则
k = next[k=0] = -1:因为t[j-1=4]=1,t[k=-1] = *,相等(通配)。所以next[j=5] = k + 1 = -1 + 1 = 0。
| Index | -1 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|
t[ ] | * | 0 | 0 | 0 | 0 | 1 | 0 |
next[] | N/A | -1 | 0 | 1 | 2 | 3 | 0 |
相关文章:
数据结构:利用递推式计算next表
next 表是 KMP 算法的核心内容,下面介绍一种计算 next 表的方法:利用递推式计算 如图 6.3.1 所示,在某一趟匹配中,当对比到最后一个字符的时候,发现匹配失败(s[i] ≠ t[j])。根据 BF 算法&…...
每日算法-250326
83. 删除排序链表中的重复元素 题目描述 思路 使用快慢指针遍历排序链表。slow 指针指向当前不重复序列的最后一个节点,fast 指针用于向前遍历探索。当 fast 找到一个与 slow 指向的节点值不同的新节点时,就将 slow 的 next 指向 fast,然后 …...
trino查询mysql报Unknown or incorrect time zone: ‘Asia/Shanghai‘
问题 trino查询mysql时报Error listing schemas for catalog mysql: java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.,trino的日志中看到Unknown or incorrect time zone…...
java学习笔记7——面向对象
关键字:static 类变量 静态变量的内存解析: 相关代码: public class ChineseTest {public static void main(String[] args) {System.out.println(Chinese.nation); //null 没赋值前System.out.println(Chinese.nation); //中国 静态变量赋值…...
leetcode day31 453+435
453 用最少数量引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂直 地…...
C++三大特性之继承
1.继承的概念及定义 回忆封装 C Stack类设计和C设计Stack对比。封装更好:访问限定符类的数据和方法放在一起 -> 避免底层接口的暴露,数据更加的安全,程序的耦合性更高迭代器的设计,封装了容器底层结构,在不暴露底层…...
PyQt QDoubleSpinBox控件用法详解
QDoubleSpinBox 是 PyQt中用于输入浮点数的控件,支持键盘输入和上下箭头调整数值。与QtSpinBox不同,QtSpinBox是用于输入整数的控件。 关键属性和方法 QDoubleSpinBox 的关键属性和方法如下表所示: 方法/属性说明setRange(min, max)设置数…...
解决Vmware 运行虚拟机Ubuntu22.04卡顿、终端打字延迟问题
亲测可用 打开虚拟机设置,关闭加速3D图形 (应该是显卡驱动的问题,不知道那个版本的驱动不会出现这个问题,所以干脆把加速关了)...
查询Marklogic数据库,因索引配置造成的返回数据count不同的问题
查询Marklogic数据库,因索引配置造成的返回数据count不同的问题 一,问题: 目前由两个MarkLogic DB,其中A表示所有的数据库统称,包含于BCD; 调用查询接口,通过A和B入口且相同的查询条件去查询B…...
ctfshow做题笔记—栈溢出—pwn73、pwn74
目录 一、pwn73(愉快的尝试一下一把梭吧!) 二、pwn74(噢?好像到现在为止还没有了解到one_gadget?) 前言: 抽空闲时间继续学习,记录了两道题,pwn74卡了几天哈哈。 一、pwn73(愉快的尝试一下一把梭吧!) …...
026-zstd
zstd 以下为Zstandard(zstd)压缩算法从原理到代码实现的技术调研报告,结合流程图、结构图及完整C代码实现: 一、核心原理与技术架构 1.1 算法原理 Zstd基于LZ77衍生算法与熵编码(FSE/Huffman)的混合架构&…...
AF3 quat_to_rot函数解读
AlphaFold3 rigid_utils 模块的 quat_to_rot 函数的功能是把四元数转换为旋转矩阵,函数利用预定义的四元数到旋转矩阵的转换表 _QTR_MAT 来简化计算。 理解四元数到旋转矩阵的转换 源代码: _quat_elements = ["a", "b", "c", "d"]…...
Elasticsearch 的搜索功能
Elasticsearch 的搜索功能 建议阅读顺序: Elasticsearch 入门Elasticsearch 搜索(本文) 1. 介绍 使用 Elasticsearch 最终目的是为了实现搜索功能,现在先将文档添加到索引中,接下来完成搜索的方法。 查询的分类&…...
Vala编成语言教程-构造函数和析构函数
构造函数 Vala支持两种略有不同的构造方案:我们将重点讨论Java/C#风格的构造方案,另一种是GObject风格的构造方案。 Vala不支持构造函数重载的原因与方法重载不被允许的原因相同,这意味着一个类不能有多个同名构造函数。但这并不构成问题&…...
Mybatis-plus配置动态多数据源
前言:微服务架构中,有些模块中可能不同组件用不同数据源,或者做数据库主从集群需要读写分离,动态切换数据源,或不同方法需要不同数据源就需要一个快捷方便的方法。引用动态数据源组件dynamic-datasource-spring-boot-s…...
CSS+JS 堆叠图片动态交互切换
结合DeepSeek提供的代码,终于实现了堆叠两张图片动态循环切换,以下是代码: 通过绝对定位放了两张图片 <div class"col-lg-5" style"z-index: 40; position: relative;"><img src"images/banner_1.png&quo…...
内存检查之Valgrind工具
内存检查之Valgrind工具 Author: Once Day Date: 2025年3月26日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-CSD…...
强大的AI网站推荐(第四集)—— Gamma
网站:Gamma 号称:展示创意的新媒介 博主评价:快速展示创意,重点是展示,在几秒钟内快速生成幻灯片、网站、文档等内容 推荐指数:🌟🌟🌟🌟🌟&#x…...
javafx项目结构+代码规范
javafx项目 1. 新建项目,对项目的理解 jdk: 是 Java Development ToolKit 的简称,也就是 Java 开发工具包。JDK 是整个 Java 的核心,包括 Java 运行环境(Java Runtime Envirnment,简称 JRE)&a…...
国外计算机证书推荐(考证)(6 Sigma、AWS、APICS、IIA、Microsoft、Oracle、PMI、Red Hat)
文章目录 证书推荐1. 六西格玛 (6 Sigma)2. 亚马逊网络服务 (AWS)3. 美国生产与库存控制学会 (APICS)4. 内部审计师协会 (IIA)5. 微软 (Microsoft)6. 甲骨文 (Oracle)7. 项目管理协会 (PMI)8. 红帽 (Red Hat) 证书推荐 1. 六西格玛 (6 Sigma) 介绍:六西格玛是一种…...
黑盒测试与白盒测试详解
黑盒测试和白盒测试是软件测试中两种最基本的测试方法,它们在测试视角、测试重点和适用场景等方面存在显著差异。 一、黑盒测试 1. 基本概念 黑盒测试又称功能测试,将软件视为一个"黑盒子",不关心内部结构和实现细节,只…...
准确--配置服务器文件数
某些系统可能在 /etc/security/limits.d/ 目录下有额外配置覆盖全局设置。检查是否存在冲突文件: ls /etc/security/limits.d/如果有文件(如 90-nproc.conf 或 90-nofile.conf),需编辑或删除这些文件中的冲突配置。 确保系统启用…...
《熔化焊接与热切割作业》考试注意事项
考试前的准备 携带必要的证件和材料:考生需携带身份证、准考证等有效证件,以及考试所需的焊接工具、材料等。确保证件齐全,避免因证件问题影响考试。 提前检查焊接设备和工具:在考试前,考生应仔细检查焊接设备和工具是…...
ROS2的发展历史、核心架构和应用场景
以下是对**ROS2(Robot Operating System 2)**的发展历史、核心架构和应用场景的详细解析,覆盖其技术演变、关键特性和生态系统: 一、ROS2的诞生背景:从ROS1到ROS2 1. ROS1的历史与局限 ROS1的起源: 2007年…...
[unity 点击事件] 区域响应点击事件,排除子节点区域,Raycast Target 应用
当我打开一个二级弹窗后,希望可以通过点击弹窗以外的区域来关闭该弹窗。一开始我是在弹窗主节点上挂载了一个 button 组件,该 button 注册的点击事件中关闭该弹窗。在子节点(一个背景图)的image组件上启用 Raycast Target 选项&am…...
鸿蒙生态全解析:应用适配分享
一、鸿蒙系统的技术底座与适配挑战 HarmonyOS NEXT 作为全场景分布式操作系统,通过统一的技术底座和声明式开发框架,实现了 "一次开发,多端部署" 的跨设备协同能力。其核心优势在于: 弹性部署架构:一套系统…...
el-select 可搜索下拉框 在ios、ipad 无法唤出键盘,造成无法输入
下一篇:el-select 可搜索下拉框,选中选项后,希望立即失去焦点,收起键盘,执行其他逻辑 【效果图】:分组展示选项 >【去界面操作体验】 首先,通过 夸克浏览器的搜索: el-select 在 ipad 输入框…...
深度剖析:域名与DNS安全的全方位解读
导语 在互联网的庞大体系中,域名如同我们访问网络资源的“门牌号”,而DNS则像是将门牌号翻译为具体地址的“翻译官”。然而,这看似平常的域名与DNS系统,却面临着诸多安全风险。一旦遭受攻击,可能导致网站无法访问、用户数据泄露等严重后果。了解域名与DNS安全知识,对保障…...
使用ZMQ和protobuf实现C++程序与Python程序的通信
文章目录 背景一 应用场景与需求二 Protobuf: 跨语言数据交换的基石三 通信方案 ZMQ (ZeroMQ) —— 高性能消息中间件四 进阶: 安全性与性能优化五 实践例子: 工厂温度监控系统5.1 场景描述5.2 Protobuf数据结构定义5.3 C数据采集与发布5.4 Python数据接收与可视化5.5 关键实现…...
Linux实现生产者消费者模型
目录 概念及优势 代码实现 概念及优势 生产者消费者模型是一种用于线程同步的模型,在这个模型中有两种角色,生产者生产数据,消费者消费数据。有三种关系,生产者与生产者,消费者与消费者,生产者与消费者。…...
