Medium
题目描述
给你一个由小写英文字母组成的字符串 s 和一个整数 k。
当前字符串 s 中的两个相同字符如果它们的索引距离不超过 k,则认为它们是相近的。
当两个字符相近时,右边的字符会合并到左边的字符中。合并一次只进行一个操作,每次合并后字符串会更新,直到不能再进行合并为止。
返回执行所有可能合并后的结果字符串。
注意:如果可以进行多个合并,总是选择左索引最小的配对进行合并。如果多个配对具有相同的最小左索引,则选择右索引最小的配对。
示例 1:
输入:s = "abca", k = 3
输出:"abc"
解释:
索引 i = 0 和 i = 3 处的字符 'a' 相近,因为 3 - 0 = 3 <= k。
将它们合并到左边的 'a',s = "abc"。
没有其他相同字符相近,所以不再进行合并。
示例 2:
输入:s = "aabca", k = 2
输出:"abca"
解释:
索引 i = 0 和 i = 1 处的字符 'a' 相近,因为 1 - 0 = 1 <= k。
将它们合并到左边的 'a',s = "abca"。
现在剩余的 'a' 字符在索引 i = 0 和 i = 3 处不相近,因为 k < 3,所以不再进行合并。
示例 3:
输入:s = "yybyzybz", k = 2
输出:"ybzybz"
解释:
索引 i = 0 和 i = 1 处的字符 'y' 相近,因为 1 - 0 = 1 <= k。
将它们合并到左边的 'y',s = "ybyzybz"。
现在索引 i = 0 和 i = 2 处的字符 'y' 相近,因为 2 - 0 = 2 <= k。
将它们合并到左边的 'y',s = "ybzybz"。
没有其他相同字符相近,所以不再进行合并。
提示:
1 <= s.length <= 1001 <= k <= s.lengths由小写英文字母组成
解题思路
这道题需要我们模拟字符合并的过程。核心思路是:
- 寻找可合并的字符对:遍历字符串,找到距离不超过k的相同字符对
- 选择合并策略:按题目要求,优先选择左索引最小的配对,如果左索引相同则选择右索引最小的配对
- 执行合并操作:将右边的字符合并到左边,即删除右边的字符
- 重复直到无法合并:每次合并后字符串会发生变化,需要重新寻找可合并的字符对
算法步骤:
- 使用一个布尔标志来控制循环,当没有发生合并时退出
- 在每轮循环中,遍历所有字符对
(i, j),其中i < j且j - i <= k - 找到字符相同且距离满足条件的最优配对(左索引最小,左索引相同时右索引最小)
- 执行合并操作后重新开始寻找
这种方法虽然时间复杂度较高,但由于字符串长度限制在100以内,完全可以接受。
推荐解法: 直接模拟法,代码简洁易懂,符合题目要求。
代码实现
class Solution {
public:
string mergeCharacters(string s, int k) {
bool merged = true;
while (merged) {
merged = false;
int bestLeft = -1, bestRight = -1;
// 找到最优的合并配对
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j < s.length() && j - i <= k; j++) {
if (s[i] == s[j]) {
if (bestLeft == -1 || i < bestLeft || (i == bestLeft && j < bestRight)) {
bestLeft = i;
bestRight = j;
}
}
}
}
// 如果找到可合并的配对,执行合并
if (bestLeft != -1) {
s.erase(bestRight, 1);
merged = true;
}
}
return s;
}
};
class Solution:
def mergeCharacters(self, s: str, k: int) -> str:
s = list(s) # 转为列表便于操作
merged = True
while merged:
merged = False
best_left, best_right = -1, -1
# 找到最优的合并配对
for i in range(len(s)):
for j in range(i + 1, min(i + k + 1, len(s))):
if s[i] == s[j]:
if best_left == -1 or i < best_left or (i == best_left and j < best_right):
best_left, best_right = i, j
# 如果找到可合并的配对,执行合并
if best_left != -1:
s.pop(best_right)
merged = True
return ''.join(s)
public class Solution {
public string MergeCharacters(string s, int k) {
var chars = s.ToCharArray().ToList();
bool merged = true;
while (merged) {
merged = false;
int bestLeft = -1, bestRight = -1;
// 找到最优的合并配对
for (int i = 0; i < chars.Count; i++) {
for (int j = i + 1; j < chars.Count && j - i <= k; j++) {
if (chars[i] == chars[j]) {
if (bestLeft == -1 || i < bestLeft || (i == bestLeft && j < bestRight)) {
bestLeft = i;
bestRight = j;
}
}
}
}
// 如果找到可合并的配对,执行合并
if (bestLeft != -1) {
chars.RemoveAt(bestRight);
merged = true;
}
}
return new string(chars.ToArray());
}
}
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var mergeCharacters = function(s, k) {
let arr = s.split('');
while (true) {
let found = false;
let minLeft = Infinity;
let minRight = Infinity;
let leftIdx = -1, rightIdx = -1;
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length && j <= i + k; j++) {
if (arr[i] === arr[j]) {
if (i < minLeft || (i === minLeft && j < minRight)) {
minLeft = i;
minRight = j;
leftIdx = i;
rightIdx = j;
found = true;
}
}
}
}
if (!found) break;
arr.splice(rightIdx, 1);
}
return arr.join('');
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n³) | 最坏情况下需要进行O(n)轮合并,每轮需要O(n²)时间寻找配对 |
| 空间复杂度 | O(n) | 需要额外空间存储字符数组 |