问题描述
题目来自leetCode:1209—删除字符串中的所有相邻重复项 II
给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。
你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。
在执行完所有删除操作后,返回最终得到的字符串。
本题答案保证唯一。
# 解题思路:
简单思路
解题思路较多,多是利用栈操作,且思路较为直白,大体上是利用栈来记录每个元素出现的次数,当超过了阈值就开始删除字符,在此不再解释。如果使用栈操作的话可以自己写个数组模拟栈,这样时间空间复杂度都会改善。
Code:耗时15ms
public String removeDuplicates(String s, int k) {
//模拟栈
int[] count = new int[32768];
int index = 0;
StringBuilder ret = new StringBuilder();
if (s.length() < k) return s;
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
char item = chars[i];
if (ret.length() > 0) {
char top = ret.charAt(ret.length()-1);
ret.append(item);
if (top == ret.charAt(ret.length()-1)) {
count[index] = count[index-1] + 1;
if (count[index] == k) {
index -= k-1;
ret.delete(ret.length()-k, ret.length());
} else index++;
} else count[index++] = 1;
} else {
ret.append(item);
count[index++] = 1;
}
}
return ret.toString();
}
Code
下面介绍一种很强的思路:
注:思路的搬运来的,因为是在提交记录里面发现的,所以没有找到是哪位大佬写的。
如果你已经对基本思路熟悉或者已经按照基本思路写出了代码并通过,那么下面这个解法就会非常非常适合你的提升,虽然是利用双指针,但是优化了实现的细节,效率仍然大大超过LeetCode官方提供的思路5。
基本思路中,耗时最多的是对字符串的操作,即使重构字符串也不如双指针的方法。
解题思路:
(图片来自LeetCode官方解题思路5)
设数组为chars[]
,i指针负责遍历整个数组(快指针),j指针负责标记满足题目要求的(不超过k个重复的)字符串的最右下标。
count[]
负责记录某个位置的连续的重复元素的个数。
解释一下这个图:
- 双指针的思路通过快慢指针避免的了大量对字符串的修改操作。j指向的不重复字符串最后一个元素的下标。
- j指针的左边是一定不满足重复条件的(当i走到某个位置时)。只有当count[j-1]置计数值为k-1,且chars[j-1] == chars[i],我们就要执行移动慢指针操作,将慢指针j回退k个位置,下次操作直接从j+1开始,相当于把这k个位置抹去了,同时也确保了从j位置往右的字符一定是满足不超过k个重复元素的。
Code
public String removeDuplicates(String s, int k) {
StringBuilder ret = new StringBuilder();
if (s == null || s.length() == 0 || s.length() < k) return s;
int[] count = new int[s.length()];
char[] chars = s.toCharArray();
// j指向的是不重复的字符串的最后一个元素。即慢指针
int j = 0;
for (int i = 0; i < chars.length; i++,j++) {
chars[j] = chars[i];
//比较慢指针指向的位置和遍历位置是否相同,如果相同则计数+1
if (j > 0 && chars[j-1] == chars[i])
count[j] = count[j-1] + 1;
else //如果不同,则计数为1
count[j] = 1;
// 检查是否有 重复的字符超过了阈值
if (count[j] == k) j -= k;
}
return new String(chars,0,j);
}