[LeetCode] 316. Remove Duplicate Letters

Given a string s, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.

Note: This question is the same as 1081: https://leetcode.com/problems/smallest-subsequence-of-distinct-characters/

Example 1:

Input: s = "cdadabcc"
Output: "adbc"

Example 2:

Input: s = "abcd"
Output: "abcd"

Example 3:

Input: s = "ecbacba"
Output: "eacb"

Example 4:

Input: s = "leetcode"
Output: "letcod"

Constraints:

  • 1 <= s.length <= 104
  • s consists of lowercase English letters.

去除重复字母。

给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicate-letters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路是单调栈。这道题的单调栈里面,我的设定是栈顶元素的字典序是最小的,栈的底部的字母的字典序是最大的。首先我们需要用一个 hashmap 记录每个字母都出现过几次,同时需要一个 hashset(seen)记录最后拼接起来的字符串中有没有出现过相同字母,还需要一个 stack 帮助比较每个字母之间的字典序,决定字母的取舍。

第一次遍历 input 字符串,用 hashmap 把每个不同字符的出现次数记录下来。

第二次遍历 input 字符串,每遍历到一个字符,你需要做如下几个动作

  • 去 hashmap 中对其出现次数 - 1
  • 如果这个字符已经被放入最后拼接的结果中了,则跳过不处理
  • 接下来用一个 while 循环判断如下内容:如果 stack 不为空,且栈顶字母的字典序比当前字母的字典序更大,同时 hashmap 中栈顶字母的出现次数没有用完,则把栈顶字母弹出
    • 这里的逻辑是如果栈顶字母字典序大但是还未用完,我们就可以弹出,否则就不能弹出,保持原顺序
  • 把当前字符入栈并在 hashset 中记录一下,说明在 output 中已经有过这个字母了

时间O(n)

空间O(n)

Java实现

 1 class Solution {
 2     public String removeDuplicateLetters(String s) {
 3         int[] count = new int[26];
 4         for (int i = 0; i < s.length(); i++) {
 5             count[s.charAt(i) - 'a']++;
 6         }
 7 
 8         Stack<Character> stack = new Stack<>();
 9         boolean[] seen = new boolean[26];
10         for (int i = 0; i < s.length(); i++) {
11             count[s.charAt(i) - 'a']--;
12             // 如果当前这个字母已经存入过结果集中,则跳过
13             if (seen[s.charAt(i) - 'a']) {
14                 continue;
15             }
16             // 如果当前stack不为空,当前字母字典序更小,且栈顶元素之后还会遇见,则开始弹出操作
17             // 这样确保字典序小的字母一直先入栈
18             while (!stack.isEmpty() && s.charAt(i) < stack.peek() && count[stack.peek() - 'a'] > 0) {
19                 seen[stack.peek() - 'a'] = false;
20                 stack.pop();
21             }
22             stack.push(s.charAt(i));
23             seen[s.charAt(i) - 'a'] = true;
24         }
25 
26         // 将字符从stack中弹出,构造成最后的结果
27         StringBuilder sb = new StringBuilder();
28         while (!stack.isEmpty()) {
29             sb.append(stack.pop());
30         }
31         return sb.reverse().toString();
32     }
33 }

 

相关题目

316. Remove Duplicate Letters

1081. Smallest Subsequence of Distinct Characters - 与316题一模一样

LeetCode 题目总结

posted @ 2020-10-07 07:19  CNoodle  阅读(488)  评论(0编辑  收藏  举报