当前位置: 首页 > 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_ 为前缀的变量才会暴露给经过 …...

Phi-4-Reasoning-Vision镜像免配置:Streamlit界面+预置参数一键启动

Phi-4-Reasoning-Vision镜像免配置&#xff1a;Streamlit界面预置参数一键启动 1. 项目概述 Phi-4-Reasoning-Vision是一款基于微软Phi-4-reasoning-vision-15B多模态大模型开发的高性能推理工具&#xff0c;专为双卡RTX 4090环境优化设计。这个工具最大的特点是开箱即用&…...

OpenClaw插件开发入门:为Qwen3-32B镜像编写天气查询技能

OpenClaw插件开发入门&#xff1a;为Qwen3-32B镜像编写天气查询技能 1. 为什么需要自定义技能&#xff1f; 去年冬天&#xff0c;我经常需要同时查看多个城市的天气来规划差旅行程。每次手动打开天气网站、输入城市名、对比数据的过程让我不胜其烦。直到我发现OpenClaw可以通…...

[搭建Web漏洞靶场:DVWA在CentOS上的部署]

//DVWA 是一个用来进行安全脆弱性鉴定的Web应用平台&#xff0c;可以手动调整靶机源代码的安全级别&#xff0c;包含暴力破解、命令行注入、跨站请求伪造、文件包含、文件上传、SQL注入、XSS等漏洞。&#xff08;1&#xff09;下载安装包通过网盘分享的文件&#xff1a;DVWA-ma…...

AI不再是聊天机器人!从《Agentic Design Patterns》汲取的5大核心启示,彻底重塑你的架构思维

大多数开发者还以为&#xff0c;生成式AI的终极答案就是把大模型参数堆得更大、提示词写得更聪明&#xff0c;就能解决一切生产力难题。但最近读完Antonio Gulli的《Agentic Design Patterns》&#xff0c;我突然意识到&#xff1a;我们过去两年其实只造出了“引擎”&#xff0…...

RWKV7-1.5B-g1a部署教程:适配A10/A100/V100等主流GPU,显存占用稳定3.8GB

RWKV7-1.5B-g1a部署教程&#xff1a;适配A10/A100/V100等主流GPU&#xff0c;显存占用稳定3.8GB 1. 模型简介 rwkv7-1.5B-g1a是基于新一代RWKV-7架构的多语言文本生成模型&#xff0c;特别适合中文场景下的轻量级应用。这个1.5B参数的版本在保持较高生成质量的同时&#xff0…...

VSCode + Clang-Format 真·无缝集成指南:不止是保存时格式化

VSCode Clang-Format 真无缝集成指南&#xff1a;不止是保存时格式化 在C/C开发中&#xff0c;代码风格一致性往往成为团队协作的痛点。当你在深夜提交代码时&#xff0c;是否曾被同事提醒"缩进不对"或"括号换行风格不一致"&#xff1f;Clang-Format作为L…...

基于ChatGPT GPTs的AI辅助开发实战:从零构建智能代码生成器

背景痛点&#xff1a;传统开发流程中的效率瓶颈 作为一名开发者&#xff0c;我们每天都在与代码打交道。但你是否也经常遇到这些令人头疼的场景&#xff1f; 需求理解偏差&#xff1a;产品经理用自然语言描述了一个复杂功能&#xff0c;你花了大半天时间反复沟通&#xff0c;…...

如何用Python脚本轻松抢到热门演唱会门票?大麦网自动抢票终极指南

如何用Python脚本轻松抢到热门演唱会门票&#xff1f;大麦网自动抢票终极指南 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 你是否曾经为抢不到心仪演唱会门票而烦恼&#…...

ChatTTS一键集成实战:从语音合成到高效部署的完整指南

最近在做一个需要语音播报功能的小项目&#xff0c;一开始觉得语音合成嘛&#xff0c;不就是调个API的事。结果真上手才发现&#xff0c;从选型、集成到上线&#xff0c;坑是一个接一个。延迟高、资源占用大、并发一上来就崩……这些问题让我头疼了好久。直到尝试了ChatTTS的“…...

1746-NR4电阻模拟输入

1746-NR4 模拟输入模块&#xff08;电阻输入&#xff09;特点由 Allen-Bradley 生产&#xff0c;属于 SLC 500 系列类型为 模拟输入模块&#xff0c;专门用于电阻信号采集提供 4 路独立输入通道支持热电偶、RTD&#xff08;热电阻&#xff09;及其他电阻传感器输入精度高&#…...