题目描述
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
提示:
- 1 <= s.length <= 104
- s 由 小写英文 字母组成
示例 :
输入:s = “cbacdcbc”
输出:“acdb”
题目分析
本题采用 栈 来维护,主要的目的是 保证字典序最小的同时,不打乱字符的相对位置
思路:
1、首先将字符串 s 通过 hash 映射到长度为 26 的数组中,统计每个 s 中每个字符出现的次数 (有很大用处) 。
2、如果待添加的字符 , 小于栈首字符 ,有三种情况讨论:
- 情况1:判断 栈首 字符 ch 在字符串 s 中后面是否还会出现 ,这里就是通过出现的次数来判断的 , 如果 s 后面仍会出现 ,即 count[ch - ‘a’] > 0 ,那么栈首元素可被删除 ;
- 情况2:如果 s 后面不会出现 , 即 count[ch - ‘a’] = 0 ,此时为了保证唯一性,不能删除 ;
- 情况3:前面两种是针对 栈首 进行判断的, 那么如果 待添加的元素 已经在 栈 里 , 那么通过唯一性 ,待添加元素 , 不能添加到 栈 内 。
class Solution {
public String removeDuplicateLetters(String s) {
int[] count = new int[26];
char[] chars = s.toCharArray();
// 统计每个字符出现的次数
for(char ch : chars){
count[ch - 'a'] ++;
}
StringBuffer result = new StringBuffer();
Stack<Character> stack = new Stack<>();
Set<Character> set = new HashSet<>(); // 这里可以用 vis[] 数组来标记是否在栈内
for(char ch : chars){
// 栈不为空,
// 1. 栈首元素大于待进栈元素且count值大于0 说明后面还有该元素,可以出栈,
// 2. 反之不能出栈,
// 3. 此外如果当前元素已经在栈里面也不需要出栈操作
while(!stack.isEmpty() && stack.peek() >= ch && count[stack.peek() - 'a'] > 0 && !set.contains(ch)) {
set.remove(stack.peek());
stack.pop();
}
count[ch - 'a'] --; // 访问过的元素,出现值需减 1
if(set.contains(ch)) continue;
set.add(ch);
stack.push(ch);
}
while(!stack.isEmpty())
result.append(stack.pop());
return result.reverse().toString();
}
}