LeetCode 去除重复字母(316题)
@author:Jingdai
@date:2020.10.27
题目描述(316题)
给你一个字符串
s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
- 示例输入
s = "bcabc"
- 示例输出
"abc"
- 提示
s 由小写英文字母组成
思路
题目大概一看可能会认为很简单,不就是去个重嘛。但仔细看会发现不是这样,题目的难点根本不是去重,而是要使字典序最小,同时不能打乱其他字符的位置。
使字典序最小,这个就类似于 LeetCode 的移掉K位数字(402题),也是利用栈,在
push
入一个新字母前,看前面的字母是否比它大,如果大的话就pop
出前面的字母,直到前面的字母比它小为止,然后push
入当前字母。这样可以保证是字典序最小了,但是又有一个新的问题,就是可能
pop
出的字母只有一个,这样就会使最后的字符串没有了这个字母,所以不能直接pop
。那应该怎么做呢?应该在pop
前检查之后还有没有同样的字母,如果有的话再pop
,这样可以保证最后不会缺少某个字母。这样就既保证最小序,又可以保证不会缺少某个字母。那问题又来了,那怎么知道后面还有没有相同的字母呢?这个就很简单了,由于题目提示明确说明了
s
由小写字母构成,可以用一个长度为26的数组记录字母的个数,每遍历到一个字母就使该字母个数减一,如果某字母对应数组中的数字为0了,说明后面没有了,就不能pop
了,否则说明后面还有,可以安全的pop
。哦,最后忘了最简单的去重,这个很好处理,直接用一个数组记录是否在栈中即可,当字母已经在栈中,则直接跳过该字母就行。代码如下。
代码
public String removeDuplicateLetters(String s) { int[] letterArray = new int[26]; for (int i = 0; i < s.length(); i++) { letterArray[s.charAt(i) - 'a']++; } boolean[] isInStack = new boolean[26]; LinkedList<Character> stack = new LinkedList<>(); for (int i = 0; i < s.length(); i++) { char tempChar = s.charAt(i); letterArray[tempChar - 'a']--; if (isInStack[tempChar - 'a']) { continue; } while (stack.size() != 0 && tempChar < stack.peek()) { if (letterArray[stack.peek() - 'a'] > 0 ) { isInStack[stack.pop() - 'a'] = false; } else { break; } } isInStack[tempChar - 'a'] = true; stack.push(tempChar); } StringBuilder ans = new StringBuilder(); while (stack.size() != 0) { ans.append(stack.removeLast()); } return ans.toString(); }