【833. 字符串中的查找与替换】

来源:力扣(LeetCode)

描述:

你会得到一个字符串 s (索引从 0 开始),你必须对它执行 k 个替换操作。替换操作以三个长度均为 k 的并行数组给出:indices, sources, targets

要完成第 i 个替换操作:

  1. 检查 子字符串 sources[i] 是否出现在 原字符串 s 的索引 indices[i] 处。
  2. 如果没有出现, 什么也不做
  3. 如果出现,则用 targets[i] 替换 该子字符串。

例如,如果 s = "abcd"indices[i] = 0 , sources[i] = "ab"targets[i] = "eee" ,那么替换的结果将是 "eeecd"

所有替换操作必须 同时 发生,这意味着替换操作不应该影响彼此的索引。测试用例保证元素间 不会重叠

  • 例如,一个 s = "abc"indices = [0, 1]sources = ["ab","bc"] 的测试用例将不会生成,因为 "ab""bc" 替换重叠。

在对 s 执行所有替换操作后返回 结果字符串

子字符串 是字符串中连续的字符序列。

示例 1:

1

输入:s = "abcd", indexes = [0,2], sources = ["a","cd"], targets = ["eee","ffff"]
输出:"eeebffff"
解释:
"a" 从 s 中的索引 0 开始,所以它被替换为 "eee""cd" 从 s 中的索引 2 开始,所以它被替换为 "ffff"

示例 2:
2

输入:s = "abcd", indexes = [0,2], sources = ["ab","ec"], targets = ["eee","ffff"]
输出:"eeecd"
解释:
"ab" 从 s 中的索引 0 开始,所以它被替换为 "eee""ec" 没有从原始的 S 中的索引 2 开始,所以它没有被替换。

提示:

  • 1 <= s.length <= 1000
  • k == indices.length == sources.length == targets.length
  • 1 <= k <= 100
  • 0 <= indexes[i] < s.length
  • 1 <= sources[i].length, targets[i].length <= 50
  • s 仅由小写英文字母组成
  • sources[i] 和 targets[i] 仅由小写英文字母组成

方法一:按照下标排序 + 模拟

思路与算法

我们直接按照题目的要求进行模拟即可。

首先我们根据数组 indices,将所有的替换操作进行升序排序。在这一步中,同时对 indices,sources,targets 这三个数组进行排序较为困难,我们可以使用一个长度(记为 m)与它们相同的数组 ops,存储 0 到 m − 1 这 m 个下标,随后对 ops 本身按照 indices 作为第一关键字进行排序即可。

在排序完成后,我们就可以遍历给定的字符串 s 进行操作了。我们使用另一个指针 pt 指向 ops 的首个元素,表示当前需要进行的操作。当我们遍历到第 i 个字符时,我们首先不断往右移动 pt,直到其移出边界,或者第 ops[pt] 个操作的下标不小于 iii。此时,会有如下的两种情况:

  • 如果这个下标大于 i,说明不存在下标为 i 的操作。我们可以直接将第 i 个字符放入答案中;
  • 如果这个下标等于 i,说明存在下标为 i 的操作。我们将 s 从位置 i 开始的长度与 sources[ops[i]] 的子串与 sources[ops[i]] 进行比较:
    • 如果相等,那么替换操作成功,我们将 targets[ops[i]] 放入答案中。由于替换操作不可能重叠,因此我们可以直接跳过 sources[ops[i]] 长度那么多数量的字符;
    • 否则,替换操作失败,我们可以直接将第 i 个字符放入答案中。

需要注意的是,题目中只保证了成功的替换操作不会重叠,而不保证失败的替换操作不会重叠。因此当这个下标等于 i 时,可能会有多个替换操作需要进行尝试,即我们需要不断往右移动 pt,直到其移出边界,或者第 ops[pt] 个操作的下标严格大于 i。遍历到的替换操作需要依次进行尝试,如果其中一个成功,那么剩余的不必尝试,可以直接退出。

代码:

class Solution {
public:string findReplaceString(string s, vector<int>& indices, vector<string>& sources, vector<string>& targets) {int n = s.size(), m = indices.size();vector<int> ops(m);iota(ops.begin(), ops.end(), 0);sort(ops.begin(), ops.end(), [&](int i, int j) { return indices[i] < indices[j]; });string ans;int pt = 0;for (int i = 0; i < n;) {while (pt < m && indices[ops[pt]] < i) {++pt;}bool succeed = false;while (pt < m && indices[ops[pt]] == i) {if (s.substr(i, sources[ops[pt]].size()) == sources[ops[pt]]) {succeed = true;break;}++pt;}if (succeed) {ans += targets[ops[pt]];i += sources[ops[pt]].size();}else {ans += s[i];++i;}}return ans;}
};

时间 0ms 击败 100.00%使用 C++ 的用户
内存 9.95mb 击败 88.18%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(n+mlog⁡m+ml),其中 n 是字符串 s 的长度,m 是数组 indices 的长度,l 是数组 sources 和 targets 中字符串的平均长度。
    - 排序需要的时间为 O(mlog⁡m);
    - 在使用双指针进行遍历的过程中,遍历字符串需要的时间为 O(n),遍历数组 ops 需要的时间为 O(m),在最坏情况下需要尝试每一个替换操作,比较和构造最终答案需要的时间为 O(ml)。
    相加即可得到总时间复杂度 O(n+mlogm+ml)。
  • 空间复杂度:O(n + ml)。
    - 数组 ops 需要的空间为 O(m);
    - 排序需要的栈空间为 O(log⁡m);
    - 在替换操作中进行比较时,如果使用的语言支持无拷贝的切片操作,那么需要的空间为 O(1),否则为 O(l);
    - 在构造最终答案时,如果使用的语言支持带修改的字符串,那么需要的空间为 O(1)(不考虑最终答案占用的空间),否则需要 O(n + ml) 的辅助空间。
    对于不同语言,上述需要的空间会有所变化。这里取每一种可能的最大值,相加即可得到总空间复杂度 O(n + ml)。

方法二:哈希表 + 模拟

思路与算法

我们也可以将方法一中的数组 ops 换成哈希映射,其中的键表示字符串中的下标,值是一个数组,存储了所有操作该下标的操作编号。我们只需要对数组 indices 进行一次遍历,就可以得到这个哈希表。

在这之后,当我们对字符串 s 进行遍历时,如果遍历到位置 i,那么哈希表中键 i 对应的数组,就是所有对位置 i 进行的操作。我们使用与方法一相同的方法处理这些操作即可。

代码:

class Solution {
public:string findReplaceString(string s, vector<int>& indices, vector<string>& sources, vector<string>& targets) {int n = s.size(), m = indices.size();unordered_map<int, vector<int>> ops;for (int i = 0; i < m; ++i) {ops[indices[i]].push_back(i);}string ans;for (int i = 0; i < n;) {bool succeed = false;if (ops.count(i)) {for (int pt: ops[i]) {if (s.substr(i, sources[pt].size()) == sources[pt]) {succeed = true;ans += targets[pt];i += sources[pt].size();break;}}}if (!succeed) {ans += s[i];++i;}}return ans;}
};

时间 0ms 击败 100.00%使用 C++ 的用户
内存 10.12mb 击败 59.09%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(n+ml),其中 n 是字符串 s 的长度,m 是数组 indices 的长度,l 是数组 sources 和 targets 中字符串的平均长度。
  • 空间复杂度:O(n+ml)。
    author:力扣官方题解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/37250.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring事务传播机制

hi ,大家好,继续为大家带来Spring事务传播机制的相关知识 文章目录 &#x1f917;1.事务传播机制是什么&#x1f917;2.事务传播机制作用&#x1f917;3.事务传播机制 &#x1f917;1.事务传播机制是什么 定义了多个包含了事务的⽅法&#xff0c;相互调⽤时&#xff0c;事务是…

[C++ 网络协议编程] UDP协议

目录 1. UDP和TCP的区别 2. UDP的工作原理 3. UDP存在数据边界 4. UDP的I/O函数 4.1 sendto函数 4.2 recvfrom函数 4. 已连接(connected)UDP套接字和未连接(unconnected)UDP套接字 5. UDP的通信流程 5.1 服务器端通信流程 5.2 客户端通信流程 1. UDP和TCP的区别 主要…

【深度学习】日常笔记16

可以将pd.DataFrame数据结构理解为类似于Excel中的表格。pd.DataFrame是pandas库提供的一个二维数据结构&#xff0c;用于存储和操作具有行和列的数据。它类似于Excel中的工作表&#xff0c;其中每一列可以是不同的数据类型&#xff08;例如整数、浮点数、字符串等&#xff09;…

关于安卓打包生成aar,jar实现(一)

关于安卓打包生成aar&#xff0c;jar方式 背景 在开发的过程中&#xff0c;主项目引入三方功能的方式有很多&#xff0c;主要是以下几个方面&#xff1a; &#xff08;1&#xff09;直接引入源代码module&#xff08;优点&#xff1a;方便修改源码&#xff0c;易于维护&#…

Spring_AOP

一、AOP简介 AOP&#xff0c;Aspect Oriented Programming,面向切面编程,是对面向对象编程0OP的升华。OOP是纵向对一个事物的抽象&#xff0c;一个对象包括静态的属性信息&#xff0c;包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象…

Vue 安装开发者工具

1.下载开发者工具&#xff0c;下载地址&#xff1a;http://book.wiyp.top/App/Vue3开发者工具-谷歌/Vue3.crx 2.打开谷歌浏览器&#xff0c;点击扩展&#xff0c;点击管理扩展程序。 3.开启开发者模式&#xff0c;将 Vue3 开发者工具文件拖拽到浏览器中进行安装。 注&#xff…

chatGPT小白快速入门培训课程-001

一、前言 本文是《chatGPT小白快速入门培训课程》的第001篇文章&#xff0c;全部内容采用chatGPT和chatGPT开源平替软件生成。完整内容大纲详见&#xff1a;《chatGPT小白快速入门课程大纲》。 本系列文章&#xff0c;参与&#xff1a; AIGC征文活动 #AIGC技术创作内容征文# …

使用pymupdf实现PDF内容搜索并显示功能

简介&#xff1a; 在日常工作和学习中&#xff0c;我们可能需要查找和提取PDF文件中的特定内容。本文将介绍如何使用Python编程语言和wxPython图形用户界面库来实现一个简单的PDF内容搜索工具。我们将使用PyMuPDF模块来处理PDF文件&#xff0c;并结合wxPython构建一个用户友好的…

虹科方案 | 汽车总线协议转换解决方案(二)

上期说到&#xff0c;虹科的PCAN-LIN网关在CAN、LIN总线转换方面有显著的作用&#xff0c;尤其是为BMS电池通信的测试提供了优秀的解决方案。假如您感兴趣&#xff0c;可以点击文末相关链接进行回顾&#xff01; 而今天&#xff0c;虹科将继续给大家带来Router系列在各个领域的…

elasticsearch 基础

ES 搜索技术历史 今天看的是《Elasticsearch实战与原理解析》 第一章 搜索技术发展史 1、搜索技术发展史 宏观而言&#xff0c;搜索引擎的发展经历了五个尖端和两大分类。五个阶段分别是ftp文件检索阶段、分类目录阶段、文本相关性检索阶段、网页链接分析阶段和用户意图识别…

【Pytorch:nn.Embedding】简介以及使用方法:用于生成固定数量的具有指定维度的嵌入向量embedding vector

文章目录 1、nn.Embedding2、使用场景 1、nn.Embedding 首先我们讲解一下关于嵌入向量embedding vector的概念 1&#xff09;在自然语言处理NLP领域&#xff0c;是将单词、短语或其他文本单位映射到一个固定长度的实数向量空间中。嵌入向量具有较低的维度&#xff0c;通常在几…

[Mongodb 5.0]单机启动

安装完mongodb后&#xff0c;会自动生成下面两个目录(mongod.conf中设定的)&#xff0c;用来存放日志和数据 /var/lib/mongo (数据目录) /var/log/mongodb (日志目录) 要启动一个单机版的mongodb&#xff0c;一般有两种方式&#xff1a; 第一种启动方式&#xff1a;直接使用…

第5章:神经网络

神经元模型 上述定义的简单单元即为神经元模型。 多层网络 误差逆传播算法 标准BP算法&#xff1a;参数更新非常频繁&#xff0c;可能出现抵消现象。积累BP算法&#xff1a;下降到一定程度上&#xff0c;进行下一步会非常缓慢。 过拟合 早停&#xff1a;划分训练集和验证集…

vue3+ts使用antv/x6

使用 2.x 版本 x6.antv 新官网: 安装 npm install antv/x6 //"antv/x6": "^2.1.6",项目结构 1、初始化画布 index.vue <template><div id"container"></div> </template><script setup langts> import { onM…

redis — 基于Spring Boot实现redis延迟队列

1. 业务场景 延时队列场景在我们日常业务开发中经常遇到&#xff0c;它是一种特殊类型的消息队列&#xff0c;它允许把消息发送到队列中&#xff0c;但不立即投递给消费者&#xff0c;而是在一定时间后再将消息投递给消费者。延迟队列的常见使用场景有以下几种&#xff1a; 在…

HoudiniVex笔记_P23_SDFBasics有向距离场

原视频&#xff1a;https://www.youtube.com/playlist?listPLzRzqTjuGIDhiXsP0hN3qBxAZ6lkVfGDI Bili&#xff1a;Houdini最强VEX算法教程 - VEX for Algorithmic Design_哔哩哔哩_bilibili Houdini版本&#xff1a;19.5 1、什么是SDF Houdini支持两种体积类型&#xff0c;…

使用wxPython和PyMuPDF提取PDF页面指定页数的内容的应用程序

在本篇博客中&#xff0c;我们将探讨如何使用wxPython和PyMuPDF库创建一个简单的Bokeh应用程序&#xff0c;用于选择PDF文件并提取指定页面的内容&#xff0c;并将提取的内容显示在文本框中。 C:\pythoncode\new\pdfgetcontent.py 准备工作 首先&#xff0c;确保你已经安装了…

大数据-玩转数据-Flink网页埋点PV统计

一、说明 衡量网站流量一个最简单的指标&#xff0c;就是网站的页面浏览量&#xff08;Page View&#xff0c;PV&#xff09;。用户每次打开一个页面便记录1次PV&#xff0c;多次打开同一页面则浏览量累计。 一般来说&#xff0c;PV与来访者的数量成正比&#xff0c;但是PV并不…

虹科干货 | 化身向量数据库的Redis Enterprise——快速、准确、高效的非结构化数据解决方案!

用户期望在他们遇到的每一个应用程序和网站都有搜索功能。然而&#xff0c;超过80%的商业数据是非结构化的&#xff0c;以文本、图像、音频、视频或其他格式存储。Redis Enterprise如何实现矢量相似性搜索呢&#xff1f;答案是&#xff0c;将AI驱动的搜索功能集成到Redis Enter…

STABLE DIFFUSION模型及插件的存放路径

记录下学习SD的一些心得&#xff0c;使用的是秋叶大佬的集成webui&#xff0c;下载了之后点击启动器即可开启&#xff0c;文件夹中的内容如下 主模型存放在models文件下的stable-diffusion文件夹内&#xff0c;一些扩展类的插件是存放在extensions文件夹下