Medium
题目描述
给你一个下标从 0 开始的字符串 s,你必须对其执行 k 个替换操作。替换操作由三个长度均为 k 的下标从 0 开始的并行数组给出:indices、sources 和 targets。
要完成第 i 个替换操作:
- 检查 sources[i] 是否出现在原字符串 s 的下标 indices[i] 处。
- 如果没有出现,什么也不做。
- 如果出现了,则用 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:
输入:s = "abcd", indices = [0, 2], sources = ["a", "cd"], targets = ["eee", "ffff"]
输出:"eeebffff"
解释:
"a" 出现在 s 中的下标 0 处,所以我们用 "eee" 替换它。
"cd" 出现在 s 中的下标 2 处,所以我们用 "ffff" 替换它。
示例 2:
输入:s = "abcd", indices = [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] 仅由小写英文字母组成
解题思路
这道题的核心思路是先处理所有的替换操作,然后重新构建字符串。
由于所有替换操作需要同时进行,不能相互影响索引,我们需要:
- 验证替换的有效性:对每个替换操作,检查在指定位置是否真的存在目标子字符串
- 按位置排序:为了便于处理,我们需要按照索引位置对替换操作进行排序
- 重新构建字符串:从左到右遍历原字符串,对于需要替换的位置使用新字符串,否则保持原字符
具体实现步骤:
- 创建一个索引到替换操作的映射,同时验证每个位置的子字符串是否匹配
- 按照索引位置排序所有有效的替换操作
- 从头开始构建新字符串,遇到替换位置时跳过原子字符串的长度,使用替换后的字符串
这种方法的时间复杂度主要来自排序和字符串构建过程,空间复杂度取决于存储替换信息和结果字符串。
代码实现
class Solution {
public:
string findReplaceString(string s, vector<int>& indices, vector<string>& sources, vector<string>& targets) {
vector<pair<int, int>> validOps;
// 验证每个替换操作并收集有效的操作
for (int i = 0; i < indices.size(); i++) {
int idx = indices[i];
if (idx + sources[i].length() <= s.length() &&
s.substr(idx, sources[i].length()) == sources[i]) {
validOps.push_back({idx, i});
}
}
// 按索引位置排序
sort(validOps.begin(), validOps.end());
string result = "";
int i = 0, opIdx = 0;
while (i < s.length()) {
if (opIdx < validOps.size() && i == validOps[opIdx].first) {
// 执行替换操作
int origOpIdx = validOps[opIdx].second;
result += targets[origOpIdx];
i += sources[origOpIdx].length();
opIdx++;
} else {
// 保持原字符
result += s[i];
i++;
}
}
return result;
}
};
class Solution:
def findReplaceString(self, s: str, indices: List[int], sources: List[str], targets: List[str]) -> str:
valid_ops = []
# 验证每个替换操作并收集有效的操作
for i in range(len(indices)):
idx = indices[i]
if idx + len(sources[i]) <= len(s) and s[idx:idx+len(sources[i])] == sources[i]:
valid_ops.append((idx, i))
# 按索引位置排序
valid_ops.sort()
result = []
i = 0
op_idx = 0
while i < len(s):
if op_idx < len(valid_ops) and i == valid_ops[op_idx][0]:
# 执行替换操作
orig_op_idx = valid_ops[op_idx][1]
result.append(targets[orig_op_idx])
i += len(sources[orig_op_idx])
op_idx += 1
else:
# 保持原字符
result.append(s[i])
i += 1
return ''.join(result)
public class Solution {
public string FindReplaceString(string s, int[] indices, string[] sources, string[] targets) {
var validOps = new List<(int index, int opIndex)>();
// 验证每个替换操作并收集有效的操作
for (int i = 0; i < indices.Length; i++) {
int idx = indices[i];
if (idx + sources[i].Length <= s.Length &&
s.Substring(idx, sources[i].Length) == sources[i]) {
validOps.Add((idx, i));
}
}
// 按索引位置排序
validOps.Sort((a, b) => a.index.CompareTo(b.index));
var result = new StringBuilder();
int pos = 0, opIdx = 0;
while (pos < s.Length) {
if (opIdx < validOps.Count && pos == validOps[opIdx].index) {
// 执行替换操作
int origOpIdx = validOps[opIdx].opIndex;
result.Append(targets[origOpIdx]);
pos += sources[origOpIdx].Length;
opIdx++;
} else {
// 保持原字符
result.Append(s[pos]);
pos++;
}
}
return result.ToString();
}
}
var findReplaceString = function(s, indices, sources, targets) {
const operations = [];
for (let i = 0; i < indices.length; i++) {
const index = indices[i];
const source = sources[i];
const target = targets[i];
if (s.substring(index, index + source.length) === source) {
operations.push({
index: index,
length: source.length,
replacement: target
});
}
}
operations.sort((a, b) => b.index - a.index);
let result = s;
for (const op of operations) {
result = result.substring(0, op.index) + op.replacement + result.substring(op.index + op.length);
}
return result;
};
复杂度分析
| 复杂度 | 值 |
|---|---|
| 时间复杂度 | O(k log k + n + m) |
| 空间复杂度 | O(k + m) |
其中:
- n 为原字符串 s 的长度
- k 为替换操作的数量
- m 为结果字符串的长度
- 时间复杂度中的 k log k 来自排序,n 来自遍历原字符串,m 来自构建结果字符串
- 空间复杂度中的 k 来自存储有效替换操作,m 来自结果字符串