力扣316
题目描述
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
字符串都由小写字母组成。
题目分析
- 需要维护一个字符串对应的Hash频数数组freq,用来存储每个元素所出现的次数。数组的下标 =字符-‘a’,表示该字母所对应的数值,存储的元素表示该字母在字符串中总共出现的次数。eg:s = abccaa, freq[a-‘a’] = 3;
- 为保证字典序尽量最小,我们可以维护一个栈。尽量单调递增栈。这样的话,出栈之后将其反序就是最小的了。如果栈顶元素 > 待入栈元素,且栈顶元素在后续中还有,就将栈顶元素出栈。
- 如果栈中已经存在了的元素,再次访问到的话就不需要入栈了,那么就需要维护一个集合set,利用set的不可重复性。
- 最后有一个点需要注意(不是太懂!!再看),是提交时一个例子不对发现的abca。因为当走到最后一个a时,前面的在栈中都是有序的,并且a已经在 集合中有了(即栈中有了),就不将该节点a入栈了。栈中也不动。
示例 1:
输入:s = “bcabc”
输出:“abc”
示例 2:
输入:s = “cbacdcbc”
输出:“acdb”
最后特例: s=“abca”
输出:“abc”
class Solution {
public String removeDuplicateLetters(String s) {
//先维护一个字符串对应的Hash频数数组,用来存储每个元素在字符串中出现的次数(这个在后续有大用处)
int[] freq = new int[26];
char[] charArrays = s.toCharArray();//将字符串变为字符数组
//维护一个 尽量单调递增栈(即后序还有该字母的情况下让其大的在上边,若后序没有该字母了就只能按字符串中顺序了),最后出栈之后反序一下就是从小到大(尽量)。
Stack<Character> stack = new Stack<>();
Set<Character> set = new HashSet<>();
for(char ch : charArrays){
freq[ch - 'a']++;
}
//定义一个字符串构造器StringBuilder
StringBuilder sb = new StringBuilder();
//尽量单调递增栈(这是理解此题的最关键字眼),
for(char ch : charArrays){
while(!stack.isEmpty() && stack.peek() >= ch && freq[stack.peek()-'a'] > 0 && !set.contains(ch)){
//如果栈顶元素 >= 让栈顶出栈,且该栈顶元素在后续中还有,则让该栈顶元素出栈且移除集合
set.remove(stack.peek());
stack.pop();
}
//被访问过的字符频数-1;
freq[ch - 'a']--;
//集合set就是为了看字符是否在栈中已经出现了,若出现了就直接跳过不再处理
if(set.contains(ch)) continue;
set.add(ch);
stack.push(ch);
}
//把栈中的字符挨个添加到字符构造器sb之后,然后再将sb反序即可得结果
while(!stack.isEmpty()){
sb.append(stack.pop());
}
sb.reverse();
return sb.toString();
}
}