携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
LeetCode 1209 删除字符串中的所有相邻重复项 II
题目描述:
给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。
你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。
在执行完所有删除操作后,返回最终得到的字符串。
本题答案保证唯一。
示例1:
输入: s = "abcd", k = 2
输出: "abcd"
解释: 没有要删除的内容。
示例2:
输入:s = "deeedbbcccbdaa", k = 3
输出:"aa"
解释:
先删除 "eee" 和 "ccc",得到 "ddbbbdaa"
再删除 "bbb",得到 "dddaa"
最后删除 "ddd",得到 "aa"
示例3:
输入: s = "pbbcggttciiippooaais", k = 2
输出: "ps"
解题思路
思路一: 暴力解法
算法:
-
记录字符串的长度。
-
遍历字符串:
-
如果当前字符与前一个相同,计数器加 1。
- 否则,重置计数器为 1。
-
如果计数器等于 k,删除这 k 个字符。
-
-
如果字符串的长度被改变,从头开始重新遍历字符串
实现代码如下:
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var removeDuplicates = function(s, k) {
let res = s, len = -1;
while (len != res.length) {
len = res.length;
for (let i = 0, count = 1; i < res.length; ++i) {
if (i == 0 || res[i] != res[i - 1]) {
count = 1;
} else if (++count == k) {
res = res.slice(0, i - k + 1) + res.slice(i + 1)
break;
}
}
}
return res;
}
思路二: 记忆计数
从方法一中可以看出,如果为每个字符设置计数器,就不必每次删除完字符后从头开始。这种方法具有线性复杂度,但需要额外空间存储字符的计数器。
算法:
-
初始数组 counts。
-
遍历字符串:
-
如果当前字符与上一个字符相等,令 counts[i] = counts[i - 1] + 1。
- 否则,令 counts[i] = 1。
-
如果 counts[i] = k,删除这 k 个字符,令 i = i - k。
-
实现代码如下:
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var removeDuplicates = function (s, k) {
let res = s,
counts = [];
for (let i = 0; i < res.length; ++i) {
if (i == 0 || res.charAt(i) != res.charAt(i - 1)) {
counts[i] = 1;
} else {
counts[i] = counts[i - 1] + 1;
if (counts[i] == k) {
res = res.slice(0, i - k + 1) + res.slice(i + 1);
i = i - k;
}
}
}
return res;
};
时间复杂度:O(n),其中 n 是字符串长度。每个字符仅被处理一次。
空间复杂度:O(n)
思路三: 栈
遍历字符串依次入栈,入栈时,判断当前元素与栈头元素是否一致,如果不一致则入栈,如果一致,判断栈头字符是否长度为 k - 1 ,如果为 k-1 ,即加入该字符时,满足连续相同字符 k 个,此时,需要栈头出栈,当前字符不进栈,如果小于 k-1 ,则取出栈头字符,加上当前字符,重新入栈。
跟LeedCode 1047: 删除字符串中的所有相邻重复项 - 掘金 (juejin.cn)一样,只是现在每次入栈 k-1 个,栈头的 k-1 个元素的第一个如果与当前准备入栈的元素相同,则代表k个元素是一样的,则无需入栈
实现代码如下:
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var removeDuplicates = function(s, k) {
if(s.length <= 1) {
return s;
}
const stack = [];
for (const ch of s) {
let prev = stack.pop();
if (!prev || prev[0] !== ch) {
stack.push(prev);
stack.push(ch);
} else if (prev.length < k - 1){
stack.push(prev + k)
}
}
return stack.join("");
};
时间复杂度: O(n),其中 n 是字符串的长度
空间复杂度: O(n)