当前位置: 首页 > news >正文

【数据结构与算法】Z算法(扩展KMP)(C++和Python写法)

Z算法(扩展KMP)

文章目录

  • Z算法(扩展KMP)
    • 朴素求法
    • 线性求法
    • 力扣类型题
      • 变种题:[3303. 第一个几乎相等子字符串的下标](https://leetcode.cn/problems/find-the-occurrence-of-first-almost-equal-substring/)

所谓Z算法,就是求一个字符串中,每个后缀子串和主串的前缀匹配字符数的数组,其也成为Z数组

eg:主串为aaaab(首位总为0,因为包含首位即本体,无意义)

  • aaaab aaab -> 3
  • aaaab aab -> 2
  • aaaab ab -> 1
  • aaaab b -> 0
  • 结果集[0, 3, 2, 1,0]

朴素求法

时间复杂度为O(n^2),暴力获取Z数组。

每次都从头匹配,如果符合往后++,不符合则返回,下一次又从头匹配。

vector<int> z_function_trivial_simple(string s)
{int n = (int)s.length();vector<int> z(n);for (int i = 1; i < n; ++i){while (i + z[i] < n && s[z[i]] == s[i + z[i]])++z[i];}return z;
}

线性求法

image-20240929233046116

我们使用一个滑动窗口[l,r],这个滑动窗口总是往右移动,我们可以称之为Z_box

这个z_box具有特性:s[l, r] = s[0, r-l](s为字符串,l和r总是从0开始)

我们再次复习一下z数组的含义:z[i]表示从s[i]开始直到末尾的子字符串和s整个字符串匹配的前缀和

问题一:如何获取这个滑动窗口?

由于滑动窗口(z_box)总是向右移动,所以我们要用z数组及i来辅助获取。

具体方法为:当i+z[i] -1 > r时,修改l和r的位置,是l = i , r = i + z[i] - 1

原因:1. 我们希望滑动窗口会比需要匹配的数字更靠后,或者说能够包含未来匹配的位置,并且滑动窗口总是往右的。

  1. i这里代表新窗口的起始位,z[i]代表匹配的长度, -1 是因为z[i]的数字里包含i的位置。

换句话说,所谓新的z_box就是更往右的匹配上的子串前缀。这么说可能比较抽象,请以下图例辅助理解:

image-20240929234003463

问题二:这个滑动窗口的具体作用?

这个滑动窗口只在i ∈[l, r]时发生作用。

我们以上图例作为一个例子,作为讲解:

  • 此时 i = 5 ,5包含在[4,6]中,而且刚好是中间

  • 因为 s[0,2] == s[4,6] ,那么z[5] 可以直接参考z[1]获取

    ​ == > 即z[i] = z[i - l]

  • 但这只是上图的可能性,因为上图中z[i-l] == 1 这个值小于r - i + 1 -> 6- 5 + 1 -> 2,我们已经知道了最多只能匹配到这里

但是!还有一种可能,就是z[i-1] == (r - i + 1),这种情况我们无法预测r后面是否可以继续匹配,那么我就需要从r的后一位开始匹配。而这种匹配方式则回到了原始的匹配中,不再进行讲解,但是这种情况我们依然可以省略已经处于滑动窗口中的匹配。

下面代码展示(如果还不理解:可以用这个网站模拟:演示Z函数)

C++ 代码

vector<int> z_function(string s)
{		vector<int> z(s.size(), 0);int l = 0, r = 0;for (int i = 1; i < s.size(); i++){if (i <= r && z[i - l] < r - i + 1){z[i] = z[i - l];}else {z[i] = max(0, r - i + 1);// 从头开始暴力求解while (i + z[i] < s.size() && s[z[i]] == s[i + z[i]])++z[i];}if (i + z[i] - 1 > r){l = i, r = i + z[i] - 1;}// 可以打印进行看看cout << "i: "<< i << ", z[i]: "<< z[i] << ", [l, r]: ["<< l <<", " << r<<"]"<<endl;}return z;
}

Python代码

def getZArray(self, s : str) -> List[int]:# z[i] 为从i开始能和主串从头匹配的字符总数z = [0] * len(s)l, r = 0, 0for i in range(1, len(s)):# 当i在窗口内# 如果z[i-l] < (r-i+1),说明z[i-l]能匹配的字符数已经可知,直接获取# 否则,有可能超出这个数字,需要从末尾继续暴力寻找if i <= r:  # i在窗口内z[i] = min(z[i - l], r - i + 1)while i + z[i] < len(s) and s[z[i]] == s[i + z[i]]:  # 暴力匹配剩余部分z[i] += 1if i + z[i] - 1 > r:  # 更新窗口边界l, r = i, i + z[i] - 1return z

力扣类型题

变种题:3303. 第一个几乎相等子字符串的下标

这道题在Z算法的基础上,变形为前缀+后缀的组合,详情可以看这篇题解,写得很好,我不班门弄斧了。贴上我的代码。

C++

class Solution {
public:int minStartingIndex(string s, string pattern) {int m = pattern.size(), n = s.size();string combine = pattern + s;reverse(pattern.begin(), pattern.end());reverse(s.begin(), s.end());string combinervs = pattern + s;vector<int> pre = getZArray(combine);			// pre_l = z[m+l]vector<int> suf = getZArray(combinervs);		// suf_r = z[m+(n-r-1)]for (int l = 0, r = m - 1; r < n; l++, r++){if (pre[m + l] + suf[m + (n - r - 1)] + 1 >= m)return l;}return -1;}private:vector<int> getZArray(string& s){vector<int> z(s.size(), 0);int l = 0, r = 0;for (int i = 1; i < s.size(); i++){if (i <= r && z[i - l] < r - i + 1){z[i] = z[i - l];}else {z[i] = max(0, r - i + 1);while (i + z[i] < s.size() && s[z[i]] == s[i + z[i]])++z[i];}if (i + z[i] - 1 > r){l = i, r = i + z[i] - 1;}}return z;}
};

Python

from typing import Listclass Solution:def getZArray(self, s: str) -> List[int]:# z[i] 是从索引 i 开始的子串与主串前缀匹配的长度z = [0] * len(s)l, r = 0, 0for i in range(1, len(s)):if i <= r:  # i在窗口内z[i] = min(z[i - l], r - i + 1)while i + z[i] < len(s) and s[z[i]] == s[i + z[i]]:  # 暴力匹配剩余部分z[i] += 1if i + z[i] - 1 > r:  # 更新窗口边界l, r = i, i + z[i] - 1return zdef minStartingIndex(self, s: str, pattern: str) -> int:m, n = len(pattern), len(s)# 生成前缀和后缀Z数组combined = pattern + sreversed_combined = pattern[::-1] + s[::-1]pre = self.getZArray(combined)suf = self.getZArray(reversed_combined)# 检查匹配位置for l in range(n - m + 1):r = l + m - 1if pre[m + l] + suf[m + (n - r - 1)] + 1 >= m:return lreturn -1

参考:

[1] Z函数(扩展KMP)

[2] 3303 第一个几乎相等子字符串的下标——题解

相关文章:

【数据结构与算法】Z算法(扩展KMP)(C++和Python写法)

Z算法&#xff08;扩展KMP&#xff09; 文章目录 Z算法&#xff08;扩展KMP&#xff09;朴素求法线性求法力扣类型题变种题&#xff1a;[3303. 第一个几乎相等子字符串的下标](https://leetcode.cn/problems/find-the-occurrence-of-first-almost-equal-substring/) 所谓Z算法&…...

免费语音转文字软件全览:开启高效记录新时代

在当今快节奏的信息时代&#xff0c;高效地处理和记录信息变得至关重要。语音转文字技术的出现&#xff0c;为我们带来了极大的便利&#xff0c;今天&#xff0c;就让我们一同探讨这些语音转文字免费的软件的使用方法。 1.365在线转文字 链接直达&#xff1a;https://www.pdf…...

PHP“===”的意义

在PHP中&#xff0c; 运算符被称为“恒等比较运算符”&#xff08;Identical Comparison Operator&#xff09;&#xff0c;它用于比较两个变量的值和类型是否完全相同。这个运算符与双等号 &#xff08;等值比较运算符&#xff09;不同&#xff0c;后者在比较时会对两边的值进…...

Tomcat架构解析

Tomcat: 是基于JAVA语言的轻量级应用服务器&#xff0c;是一款完全开源免费的Servlet服务器实现。 1. 总体设计 socket: 其实就是操作系统提供给程序员操作“网络协议栈”的接口&#xff0c;你能通过socket的接口&#xff0c;来控制协议&#xff0c;实现网络通信&#xff0c;达…...

如何在 Kubernetes 上部署和配置开源数据集成平台 Airbyte?

在 Kubernetes 上部署和配置 Airbyte 是一个复杂但非常有价值的过程&#xff0c;特别是对于需要强大数据集成和数据处理能力的企业或团队。Airbyte 是一个开源的数据集成平台&#xff0c;允许用户从各种来源提取数据并加载到目标存储中。其强大的插件系统支持多种数据源与目标&…...

信息技术与商业变革:机遇与挑战

信息技术与商业变革&#xff1a;机遇与挑战 目录 引言信息技术推动商业变革的主要因素 数字化转型的加速客户需求的个性化创新技术的应用 信息技术在企业中的应用场景 供应链管理的智能化营销与客户关系管理财务与资源管理的自动化远程工作和协作 信息技术带来的挑战 网络安全…...

JavaWeb之过滤器

1. 过滤器的概念 过滤器是Java Servlet规范中定义的组件&#xff0c;用于在请求到达Servlet之前或响应返回客户端之前&#xff0c;对请求或响应进行拦截和处理。过滤器可以实现以下功能&#xff1a; 日志记录&#xff1a;记录请求的详细信息&#xff0c;如URI、参数、时间等。…...

学习 笔记

bin log/redo log/undo log MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 bin log&#xff08;二进制日志&#xff09;和 redo log&#xff08;重做日志&#xff09;和 undo log&#xff08;回滚日志&#xff09;。 慢SQL查询&…...

Flask-1

文章目录 Flask准备创建flask项目flask加载项目配置的二种方式 路由的基本定义接收任意路由参数接收限定类型参数自定义路由参数转换器 终端运行Flask项目http的请求与响应flask的生命周期请求获取请求中各项数据获取请求URL参数获取请求体获取请求头相关信息 响应响应html文本…...

pve 直通硬盘

qm set <vm_id> –<disk_type>[n] /dev/disk/by-id/- b r a n d − brand- brand−model_$serial_number <vm_id> : 为创建虚拟机时指定的VM ID。 <disk_type>[n]&#xff1a; 导入后的磁盘的总线类型及其编号&#xff0c;总线类型可以选择IDE、SATA…...

NLP_情感分类_机器学习(w2v)方案

文章目录 项目背景数据清洗导包导入数据切分评论及标签Word2Vec构造w2v 数据切分模型训练查看结果 同类型项目 项目背景 项目的目的&#xff0c;是为了对情感评论数据集进行预测打标。在训练之前&#xff0c;需要对数据进行数据清洗环节&#xff0c;前面已对数据进行清洗&…...

240929-CGAN条件生成对抗网络

240929-CGAN条件生成对抗网络 前面我们学习了GAN&#xff08;240925-GAN生成对抗网络-CSDN博客&#xff09;和DCGAN&#xff08;240929-DCGAN生成漫画头像-CSDN博客&#xff09;&#xff0c;接下来继续来看CGAN&#xff08;Conditional GAN&#xff09;条件生成对抗网络。 流…...

springboot第74集:设计模式

解析 核心线程数与CPU核数相同&#xff1a;避免线程过多导致的上下文切换&#xff0c;提高CPU利用率。无界队列&#xff1a;适合任务量大且任务执行时间短的场景&#xff0c;避免因队列满而拒绝任务。 IO密集型任务 场景描述 适用于执行大量IO操作的任务&#xff0c;如文件读写…...

数字化采购管理革新:全过程数字化采购管理平台的架构与实施

摘要&#xff1a;在数字化转型的浪潮中&#xff0c;采购管理正逐步迈向全流程的数字化。本文将详细解析全过程数字化采购管理平台的技术架构和实施策略&#xff0c;探讨如何通过Spring Cloud、Spring Boot2、Mybatis等先进技术和服务框架&#xff0c;实现从供应商管理到采购招投…...

Webpack 特性探讨:CDN、分包、Tree Shaking 与热更新

文章目录 前言包准备CDN 集成代码分包Tree Shaking原理实现条件&#xff1a;解决 treeShaking 无效方案&#xff1a;示例代码&#xff1a; 热更新&#xff08;HMR&#xff09; 前言 Webpack 作为现代前端开发中的核心构建工具&#xff0c;提供了丰富的特性来帮助开发者优化和打…...

Robot Operating System——一组三维空间中的位姿(位置和方向)

大纲 应用场景1. 机器人导航场景描述具体应用 2. 运动规划场景描述具体应用 3. 物体识别和跟踪场景描述具体应用 4. 环境建模场景描述具体应用 5. 仿真环境场景描述具体应用 定义字段解释 案例 geometry_msgs::msg::PoseArray 是 ROS 2 中的一个消息类型&#xff0c;用于表示一…...

mycat读写分离中间件

5、部署Mycat读写分离中间件服务 5.1安装Mycat服务 将Mycat服务的二进制软件包Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz上传到Mycat虚拟机的/root目录下&#xff0c;并将软件包解压到/use/local目录中 5.2赋予解压后的mycat目录权限 5.3向/etc/profile系统变量…...

Growthly Quest 增长工具:助力 Web3 项目实现数据驱动的增长

作者&#xff1a;Stella L (stellafootprint.network) 在瞬息万变的 Web3 领域&#xff0c;众多项目在用户吸引、参与和留存方面遭遇重重难关。Footprint Analytics 推出 Growthly&#xff0c;作为应对这些挑战的全方位解决方案&#xff0c;其中创新性的 Quest&#xff08;任务…...

Pytorch 学习手册

零 相关资料 官方网址 官方网址下的API搜索网站 一 定义 深度学习框架是用于设计、训练和部署深度学习模型的软件工具包。这些框架提供了一系列预定义的组件&#xff0c;如神经网络层&#xff08;卷积层、全连接层等&#xff09;、损失函数、优化器以及数据处理工具&#xf…...

第十一章 【前端】调用接口(11.1)——Vite 环境变量

第十一章 【前端】调用接口 11.1 Vite 环境变量 参考&#xff1a;https://cn.vitejs.dev/guide/env-and-mode.html Vite 在一个特殊的 import.meta.env 对象上暴露环境变量。为了防止意外地将一些环境变量泄漏到客户端&#xff0c;只有以 VITE_ 为前缀的变量才会暴露给经过 …...

为AI智能体构建持久记忆系统:Claw Recall部署与MCP集成指南

1. 项目概述&#xff1a;为AI智能体构建持久、可搜索的记忆系统如果你和我一样&#xff0c;深度使用Claude Code、OpenClaw这类AI智能体工具进行日常开发&#xff0c;那你一定遇到过这个让人头疼的问题&#xff1a;对话上下文被压缩&#xff08;Context Compaction&#xff09;…...

IP核验证责任共担模型:从授权方到被授权方的实践策略

1. IP核验证的责任边界&#xff1a;一场持续多年的行业对话在SoC设计领域&#xff0c;IP核的集成与验证从来都不是一个轻松的话题。随着芯片设计复杂度的指数级增长&#xff0c;一个现代SoC中可能集成了数十甚至上百个来自不同供应商的IP核&#xff0c;从处理器、内存控制器到各…...

构建个人技能知识库:从Markdown管理到自动化实践

1. 项目概述&#xff1a;一个技能库的诞生与价值最近在整理个人知识体系时&#xff0c;我一直在思考一个问题&#xff1a;如何将那些零散的、跨领域的“技能点”系统化地管理起来&#xff0c;形成一个可以持续迭代、随时取用的个人工具箱&#xff1f;这不仅仅是写一份简历上的技…...

告别盲选!深入解读5G NR中UCI偏置值(beta_offset)的配置策略与索引选择

5G NR中UCI偏置值配置的工程实践指南 在5G新空口(NR)系统中&#xff0c;上行控制信息(UCI)通过物理上行共享信道(PUSCH)传输时&#xff0c;其资源分配直接影响到系统性能和用户体验。作为网络优化工程师&#xff0c;我们经常需要面对各种复杂的配置场景&#xff0c;而UCI偏置值…...

心灵鸡汤01 - 人生九不争

一、跟父母&#xff0c;不争口舌&#xff1b; 二、跟朋友&#xff0c;不争面子&#xff1b; 三、跟领导&#xff0c;不争高低&#xff1b; 四、跟小人&#xff0c;不争道理&#xff1b; 五、跟伴侣&#xff0c;不争对错&#xff1b; 六、跟亲戚&#xff0c;不争穷富&#xff1b…...

为AI编程助手构建持久化项目记忆库:告别上下文遗忘,提升团队协作效率

1. 项目概述&#xff1a;为AI编程助手构建持久化项目记忆库如果你和我一样&#xff0c;每天都要和Claude Code、Cursor这些AI编程助手打交道&#xff0c;肯定遇到过这个烦人的问题&#xff1a;每次新开一个对话&#xff0c;AI就像得了失忆症&#xff0c;完全不记得你刚才在做什…...

【管理科学】【财务领域】【社会科学】人的需求来源和由需求诞生的企业/业务/行业及其上游产业链/中游产业链/下游产业链的所有内容03

编号 类型 (核心功能) 人的需求类型 (对应场景) 人需求得以满足的信息产品/实体产品/制造加工工具/原材料/其他 由需求诞生的企业/业务/行业及其上游产业链/中工产业链/下游产业链的所有内容及多学科数学建模方程式​ /时序数学方程式及货币来源及业务财务模型 流动时序方程…...

从原型到优化:基于LoRa SX1278与STM32的音频对讲系统实战剖析

1. 项目背景与原型机搭建 记得第一次用STM32F103C8T6驱动LoRa SX1278模块时&#xff0c;手边只有个简易麦克风模块和杜邦线。当时就想做个能传语音的无线对讲系统&#xff0c;没想到后来踩了这么多坑。这个项目最核心的三部分就是ADC采集声音、SPEEX压缩音频、LoRa传输数据&am…...

用Python和statsmodels搞定因果推断:手把手教你实现边缘结构模型(MSM)

Python实战&#xff1a;用边缘结构模型(MSM)破解纵向数据因果推断难题 在医疗健康、社会科学和商业分析领域&#xff0c;我们经常面临一个核心挑战&#xff1a;如何从观察性数据中得出可靠的因果结论&#xff1f;当数据具有时间维度时——比如患者的多次就诊记录、用户的连续行…...

【UWB-IMU、UWB定位】【UWB-IMU】融合仅具有测距和6轴IMU传感器数据的位置信息研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...