【数据结构与算法】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;
}
线性求法

我们使用一个滑动窗口[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. 我们希望滑动窗口会比需要匹配的数字更靠后,或者说能够包含未来匹配的位置,并且滑动窗口总是往右的。
- i这里代表新窗口的起始位,z[i]代表匹配的长度, -1 是因为z[i]的数字里包含i的位置。
换句话说,所谓新的z_box就是更往右的匹配上的子串前缀。这么说可能比较抽象,请以下图例辅助理解:

问题二:这个滑动窗口的具体作用?
这个滑动窗口只在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算法(扩展KMP) 文章目录 Z算法(扩展KMP)朴素求法线性求法力扣类型题变种题:[3303. 第一个几乎相等子字符串的下标](https://leetcode.cn/problems/find-the-occurrence-of-first-almost-equal-substring/) 所谓Z算法&…...
免费语音转文字软件全览:开启高效记录新时代
在当今快节奏的信息时代,高效地处理和记录信息变得至关重要。语音转文字技术的出现,为我们带来了极大的便利,今天,就让我们一同探讨这些语音转文字免费的软件的使用方法。 1.365在线转文字 链接直达:https://www.pdf…...
PHP“===”的意义
在PHP中, 运算符被称为“恒等比较运算符”(Identical Comparison Operator),它用于比较两个变量的值和类型是否完全相同。这个运算符与双等号 (等值比较运算符)不同,后者在比较时会对两边的值进…...
Tomcat架构解析
Tomcat: 是基于JAVA语言的轻量级应用服务器,是一款完全开源免费的Servlet服务器实现。 1. 总体设计 socket: 其实就是操作系统提供给程序员操作“网络协议栈”的接口,你能通过socket的接口,来控制协议,实现网络通信,达…...
如何在 Kubernetes 上部署和配置开源数据集成平台 Airbyte?
在 Kubernetes 上部署和配置 Airbyte 是一个复杂但非常有价值的过程,特别是对于需要强大数据集成和数据处理能力的企业或团队。Airbyte 是一个开源的数据集成平台,允许用户从各种来源提取数据并加载到目标存储中。其强大的插件系统支持多种数据源与目标&…...
信息技术与商业变革:机遇与挑战
信息技术与商业变革:机遇与挑战 目录 引言信息技术推动商业变革的主要因素 数字化转型的加速客户需求的个性化创新技术的应用 信息技术在企业中的应用场景 供应链管理的智能化营销与客户关系管理财务与资源管理的自动化远程工作和协作 信息技术带来的挑战 网络安全…...
JavaWeb之过滤器
1. 过滤器的概念 过滤器是Java Servlet规范中定义的组件,用于在请求到达Servlet之前或响应返回客户端之前,对请求或响应进行拦截和处理。过滤器可以实现以下功能: 日志记录:记录请求的详细信息,如URI、参数、时间等。…...
学习 笔记
bin log/redo log/undo log MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 bin log(二进制日志)和 redo log(重做日志)和 undo log(回滚日志)。 慢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]: 导入后的磁盘的总线类型及其编号,总线类型可以选择IDE、SATA…...
NLP_情感分类_机器学习(w2v)方案
文章目录 项目背景数据清洗导包导入数据切分评论及标签Word2Vec构造w2v 数据切分模型训练查看结果 同类型项目 项目背景 项目的目的,是为了对情感评论数据集进行预测打标。在训练之前,需要对数据进行数据清洗环节,前面已对数据进行清洗&…...
240929-CGAN条件生成对抗网络
240929-CGAN条件生成对抗网络 前面我们学习了GAN(240925-GAN生成对抗网络-CSDN博客)和DCGAN(240929-DCGAN生成漫画头像-CSDN博客),接下来继续来看CGAN(Conditional GAN)条件生成对抗网络。 流…...
springboot第74集:设计模式
解析 核心线程数与CPU核数相同:避免线程过多导致的上下文切换,提高CPU利用率。无界队列:适合任务量大且任务执行时间短的场景,避免因队列满而拒绝任务。 IO密集型任务 场景描述 适用于执行大量IO操作的任务,如文件读写…...
数字化采购管理革新:全过程数字化采购管理平台的架构与实施
摘要:在数字化转型的浪潮中,采购管理正逐步迈向全流程的数字化。本文将详细解析全过程数字化采购管理平台的技术架构和实施策略,探讨如何通过Spring Cloud、Spring Boot2、Mybatis等先进技术和服务框架,实现从供应商管理到采购招投…...
Webpack 特性探讨:CDN、分包、Tree Shaking 与热更新
文章目录 前言包准备CDN 集成代码分包Tree Shaking原理实现条件:解决 treeShaking 无效方案:示例代码: 热更新(HMR) 前言 Webpack 作为现代前端开发中的核心构建工具,提供了丰富的特性来帮助开发者优化和打…...
Robot Operating System——一组三维空间中的位姿(位置和方向)
大纲 应用场景1. 机器人导航场景描述具体应用 2. 运动规划场景描述具体应用 3. 物体识别和跟踪场景描述具体应用 4. 环境建模场景描述具体应用 5. 仿真环境场景描述具体应用 定义字段解释 案例 geometry_msgs::msg::PoseArray 是 ROS 2 中的一个消息类型,用于表示一…...
mycat读写分离中间件
5、部署Mycat读写分离中间件服务 5.1安装Mycat服务 将Mycat服务的二进制软件包Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz上传到Mycat虚拟机的/root目录下,并将软件包解压到/use/local目录中 5.2赋予解压后的mycat目录权限 5.3向/etc/profile系统变量…...
Growthly Quest 增长工具:助力 Web3 项目实现数据驱动的增长
作者:Stella L (stellafootprint.network) 在瞬息万变的 Web3 领域,众多项目在用户吸引、参与和留存方面遭遇重重难关。Footprint Analytics 推出 Growthly,作为应对这些挑战的全方位解决方案,其中创新性的 Quest(任务…...
Pytorch 学习手册
零 相关资料 官方网址 官方网址下的API搜索网站 一 定义 深度学习框架是用于设计、训练和部署深度学习模型的软件工具包。这些框架提供了一系列预定义的组件,如神经网络层(卷积层、全连接层等)、损失函数、优化器以及数据处理工具…...
第十一章 【前端】调用接口(11.1)——Vite 环境变量
第十一章 【前端】调用接口 11.1 Vite 环境变量 参考:https://cn.vitejs.dev/guide/env-and-mode.html Vite 在一个特殊的 import.meta.env 对象上暴露环境变量。为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_ 为前缀的变量才会暴露给经过 …...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
Python爬虫实战:研究Restkit库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...
生信服务器 | 做生信为什么推荐使用Linux服务器?
原文链接:生信服务器 | 做生信为什么推荐使用Linux服务器? 一、 做生信为什么推荐使用服务器? 大家好,我是小杜。在做生信分析的同学,或是将接触学习生信分析的同学,<font style"color:rgb(53, 1…...
