316. 去除重复字母
316. 去除重复字母
题目
原题 316. 去除重复字母
解题
题目中告知需要维持字典序并去除重复的字母,能够想到需要使用单调栈的思路,下面以 1 个具体例子分析问题:
给定字符串 s="cbacdcbc"
:
'c'
直接入栈,当前栈中字符串为"c"
'b'
等待入栈,发现栈顶字符为'c'
,'c'
>'b'
并且'b'
后面的这些字符中仍旧存在多个'c'
字符,我们可以安全的把栈顶的'c'
删除,于是栈变成了"b"
'a'
等待入栈,发现栈顶字符为'b'
,'b'
>'a'
并且'a'
后面的这些字符中仍旧存在'b'
字符,我们可以安全的删除栈顶'b'
,于是栈变成了"a"
'c'
等待入栈,发现栈顶字符是'a'
,'a'
<'c'
,那么直接入栈,不需要维护单调栈,栈变成了"ac"
'd'
等待入栈,发现栈顶字符是'c'
,'c'
<'d'
,那么直接入栈,不需要维护单调栈,栈变成了"acd"
'c'
等待入栈,栈中已经存在了字符'c'
,所以'c'
不能够入栈,栈仍旧是"acd"
,等待入栈的'c'
被丢弃'b'
等待入栈,发现栈顶字符是'd'
,'d'
>'b'
,但是等待入栈的字符'b'
后面并不存在任何字符'd'
了,所以栈顶的'd'
不能被删除,那么直接入栈,栈变成了"acdb"
'c'
等待入栈,栈中已经存在了字符'c'
,所以'c'
不能够入栈,栈仍旧是"acdb"
,等待入栈的'c'
被丢弃
根据分析问题的思路我们能够知道,需要使用一些变量去记录待入栈 c 字符的右侧任意字符剩余数量,某字符是否存在于栈中 关于如何理解这个字符剩余数量呢?下面是一些 tips:
- 初始化的
remains[]
记录了字符的总出现次数 - 一旦字符
c
被选择了,不管它是否入栈了(后续被入栈,或者被丢弃),都应该直接将这个字符剩余可用数量remains[c] - 1
单调栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public String removeDuplicateLetters(String s) {
// 字符出现的次数
int[] remains = new int[26];
// 构造单调栈过程中,某个字符串是否在栈中存在
boolean[] exists = new boolean[26];
char[] chars = s.toCharArray();
for (char c : chars) {
remains[c - 'a']++;
}
StringBuilder sb = new StringBuilder();
for (char c : chars) {
// 选中了字符串 c, 从 remains 里面移除字符串 c
remains[c - 'a']--;
// 如果栈中已经存在字符 c, 就直接跳过
if (exists[c - 'a'])
continue;
// 维护单调栈:栈顶字符 > 等待入栈的字符 c, 并且栈顶字符 remains>0, 说明可以被替换掉
while (!sb.isEmpty() && sb.charAt(sb.length() - 1) > c && remains[sb.charAt(sb.length() - 1) - 'a'] > 0) {
exists[sb.charAt(sb.length() - 1) - 'a'] = false;
sb.deleteCharAt(sb.length() - 1);
}
// 将字符 c 入栈
sb.append(c);
exists[c - 'a'] = true;
}
return sb.toString();
}
本文由作者按照 CC BY 4.0 进行授权