1.题目描述
去除重复字母
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例 1:
输入:s = "bcabc"
输出:"abc"
示例 2:
输入:s = "cbacdcbc"
输出:"acdb"
2.思路
2.1 代码
从题目中可以分析出题目要求主要有以下三点:
- 去除重复字母
- 保留字符串字典序最小
- 不能打乱字符相对位置
考虑这两点,可以使用单调栈来进行解答,最后得到的栈中元素即为答案。使用单调栈时,遍历字符串将字符压入栈中。在遍历字符时,需要对栈顶元素进行弹出,弹出条件有以下四个:
- 栈不为空
- 栈顶元素大于当前字符(确保字典序小)
- 字符串中后面部分还有当前字符(确保每个字符都存在,不会出现字符多删的情况)
- 栈中不包含当前字符(确保字符相对位置不变),并且只有当栈中没有当前字符时,才会压入栈中
综合以上思路,使用三个数组来进行作答,分别是 stack 栈数组、统计字符串中字符使用个数的 count 数组、统计栈中字符个数的数字 stackCount。在每次入栈时,字符在 stackCount 对应位置上的计数加 1 ;在每次弹出栈时,字符在 stackCount 对应位置上的计数减 1 。每次遍历将字符对应 count 中位置计数减 1 。遍历完成之后弹出栈中所有元素,并使用 StringBuilder 进行拼接。代码如下:
class Solution {
public String removeDuplicateLetters(String s) {
int[] count = new int[26];
for (int i = 0; i < s.length(); i++) {
count[s.charAt(i) - 'a']++;
}
char[] stack = new char[s.length()];
int[] stackCount = new int[26];
int index = -1;
for (int i = 0; i < s.length(); i++) {
while (index != -1 && stack[index] > s.charAt(i) && count[stack[index] - 'a'] > 0 && stackCount[s.charAt(i) - 'a'] == 0) {
stackCount[stack[index] - 'a']--;
index--;
}
if (stackCount[s.charAt(i) - 'a'] == 0) {
stack[++index] = s.charAt(i);
stackCount[s.charAt(i) - 'a']++;
}
count[s.charAt(i) - 'a']--;
}
StringBuilder builder = new StringBuilder();
while (index != -1) {
builder.append(stack[index--]);
}
return builder.reverse().toString();
}
}
2.2 测试结果
通过测试
3.总结
- 通过分析题目,考虑使用单调栈进行解答
- 这道题目比较绕,并且入栈和出栈的条件比较多
- 使用数组来存放字符剩余情况和栈中使用字符情况,能减少程序耗时
- 只有在栈中不包含当前字符时,才将字符入栈