给你一个字符串 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 <= s.length <= 10^5
2 <= k <= 10^4
s
中只含有小写英文字母。
提示 1
Use a stack to store the characters, when there are k same characters, delete them.
提示 2
To make it more efficient, use a pair to store the value and the count of each character.
解法1:记忆计数
如果为每个字符设置计数器,就不必每次删除完字符后从头开始。这种方法具有线性复杂度,但需要额外空间存储字符的计数器。
1.初始长度为 n 的数组 counts。
2.遍历字符串:
- 如果当前字符与上一个字符相等,令 counts[i] = counts[i - 1] + 1。
- 否则,令 counts[i] = 1。
- 如果 counts[i] = k,删除这 k 个字符,令 i = i - k。
Java版:
class Solution {
public String removeDuplicates(String s, int k) {
StringBuilder st = new StringBuilder(s);
int[] counts = new int[st.length()];
for (int i = 0; i < st.length(); i++) {
if (i == 0 || st.charAt(i) != st.charAt(i - 1)) {
counts[i] = 1;
} else {
counts[i] = counts[i - 1] + 1;
if (counts[i] == k) {
st.delete(i - k + 1, i + 1);
i = i - k;
}
}
}
return st.toString();
}
}
Python3版:
class Solution:
def removeDuplicates(self, s: str, k: int) -> str:
counts = [0] * len(s)
i = 0
while i < len(s):
if i == 0 or s[i] != s[i - 1]:
counts[i] = 1
else:
counts[i] = counts[i - 1] + 1
if counts[i] == k:
# [0, i - k] [i - k + 1, i] [i + 1, n - 1]
s = s[:i - k + 1] + s[i + 1:]
i = i - k
i += 1
return s
复杂度分析
-
时间复杂度:O(n),其中 n 是字符串长度。每个字符仅被处理一次。
-
空间复杂度:O(n),存储每个字符的计数器。
解法2:栈
当前字符与前一个不同时,往栈中压入 1。否则栈顶元素加 1。
- 迭代字符串:
- 如果当前字符与前一个相同,栈顶元素加 1;否则,往栈中压入 1。
- 如果栈顶元素等于 k,则从字符串中删除这 k 个字符,并将 k 从栈顶移除。
注意:因为在 Java 中 Integer 是不可变的,需要先弹出栈顶元素,然后加 1,再压入栈顶。
Java版:
class Solution {
public String removeDuplicates(String s, int k) {
StringBuilder st = new StringBuilder(s);
ArrayDeque<Integer> counts = new ArrayDeque<>();
for (int i = 0; i < st.length(); i++) {
if (i == 0 || st.charAt(i) != st.charAt(i - 1)) {
counts.push(1);
} else {
int inc = counts.pop() + 1;
if (inc == k) {
st.delete(i - k + 1, i + 1);
i = i - k;
} else {
counts.push(inc);
}
}
}
return st.toString();
}
}
Python3版:
class Solution:
def removeDuplicates(self, s: str, k: int) -> str:
counts = []
i = 0
while i < len(s):
if i == 0 or s[i] != s[i - 1]:
counts.append(1)
else:
counts[-1] += 1
if counts[-1] == k:
counts.pop()
s = s[:i - k + 1] + s[i + 1:]
i = i - k
i += 1
return s
复杂度分析
-
时间复杂度:O(n),其中 n 是字符串长度。每个字符只处理一次。
-
空间复杂度:O(n),栈空间。
解法3:栈重建
如果将计数器和字符都存储在栈中,则不需要修改字符串,只需要根据栈中结果重建字符串即可。
- 迭代字符串:
- 如果当前字符与栈顶元素相同,则栈顶元素计数器加 1;否则,计数器设为 1,当前字符压入栈。
- 如果栈顶元素计数器等于 k,则弹出栈顶元素。
- 使用栈中元素和计数器构建结果字符串。
Java版:
class Pair {
int cnt;
char ch;
public Pair(int cnt, char ch) {
this.cnt = cnt;
this.ch = ch;
}
}
class Solution {
public String removeDuplicates(String s, int k) {
ArrayDeque<Pair> counts = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
if (counts.isEmpty() || s.charAt(i) != counts.peek().ch) {
counts.push(new Pair(1, s.charAt(i)));
} else {
if (++counts.peek().cnt == k) {
counts.pop();
}
}
}
StringBuilder st = new StringBuilder();
while (!counts.isEmpty()) {
Pair p = counts.pop();
while (p.cnt-- != 0) {
st.append(p.ch);
}
}
return st.reverse().toString();
}
}
Python3版:
class Pair:
def __init__(self, cnt, ch):
self.cnt = cnt
self.ch = ch
class Solution:
def removeDuplicates(self, s: str, k: int) -> str:
counts = []
for ch in s:
if not counts or ch != counts[-1].ch:
counts.append(Pair(1, ch))
else:
counts[-1].cnt += 1
if counts[-1].cnt == k:
counts.pop()
st = []
for x in counts:
st.append(x.ch * x.cnt )
return ''.join(st)
复杂度分析
-
时间复杂度:O(n),其中 n 是字符串长度。每个字符只处理一次。
-
空间复杂度:O(n),栈空间。