【经典算法题】去除重复字母
Leetcode 0316 去除重复字母
题目描述:Leetcode 0316 去除重复字母
分析
-
本题的考点:栈、贪心算法。
-
这里使用栈解决该问题,从左向右遍历
s
,结果存储在栈stk
中,使用哈希表ins
表示某个字符当前是否在stk
中,为了判断后面是否还存在某个字符,还需要一个哈希表last
表示某个字符在s
中最后出现的位置。 -
假设当前遍历到
s[i]
,如果栈顶元素是c
,并且c>s[i]
且s
中后面的字符还存在c
,则可以将c
删掉,继续考虑下一个栈顶元素,这样得到的结果一定最好,因为删掉的字符都大于s[i]
。 -
如果
s[i]
在栈中出现过了,因为每个字母只能被保留一次,继续遍历下一个字符即可。
代码
- C++
class Solution {
public:
string removeDuplicateLetters(string s) {
string stk;
// in stack : 用于判断某个字符是否在stk中
unordered_map<char, bool> ins;
// 用于存储每个字符在 s 中出现的最后位置,方便判断后面是否还有该字符,用哈希表统计次数也可以
unordered_map<char, int> last;
for (int i = 0; i < s.size(); i++) last[s[i]] = i;
for (int i = 0; i < s.size(); i++) {
if (ins[s[i]]) continue;
// 栈非空,栈顶元素大于当前考察元素,后面还存在s[i]
while (stk.size() && stk.back() > s[i] && last[stk.back()] > i) {
ins[stk.back()] = false;
stk.pop_back();
}
stk += s[i];
ins[s[i]] = true;
}
return stk;
}
};
- Java
class Solution {
public String removeDuplicateLetters(String s) {
char[] cs = s.toCharArray();
Deque<Character> stk = new ArrayDeque<>();
boolean[] ins = new boolean[26];
int[] last = new int[26];
for (int i = 0; i < cs.length; i++) last[cs[i] - 'a'] = i;
for (int i = 0; i < cs.length; i++) {
if (ins[cs[i] - 'a']) continue;
while (!stk.isEmpty() && stk.peek() > cs[i] && last[stk.peek() - 'a'] > i) ins[stk.pop() - 'a'] = false;
stk.push(cs[i]);
ins[cs[i] - 'a'] = true;
}
StringBuilder sb = new StringBuilder();
while (!stk.isEmpty()) sb.append(stk.pop());
return sb.reverse().toString();
}
}
时空复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),
n
为字符串长度。 -
空间复杂度: O ( n ) O(n) O(n)。