Loading

Leetcode316.去除重复字母

316. 去除重复字母

Difficulty: 中等

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

注意:该题与 1081 相同

示例 1:

输入:s = "bcabc"
输出:"abc"

示例 2:

输入:s = "cbacdcbc"
输出:"acdb"

提示:

  • 1 <= s.length <= 10<sup>4</sup>
  • s 由小写英文字母组成

Solution

思路:栈的使用。官方题解(视频思路很清楚)
字符串如何判断字典序大小呢,先比较字符串首字符在字典出现的次序,越早出现,字符串字典序越小,如果一样,那么就比较下一个字符的出现次序。
针对此题,因为字符长度最为\(10^4\),因此,最好只遍历一遍字符串,否则时间将会超时。那么遍历字符串时,先设置一个存放最终结果的list,当遍历到第i个字符时,

  • step1:那么判断第i-1个字符是否在位置i之后还会出现:
  • step1.1: 如果不会出现,那么就无法改变字典序,只能将第i个字符加入list中;
  • step1.2:如果会出现,那么要判断在后面出现时的字典序会不会比在第i-1位置出现的字典序来得小:
  • step1.2.1:如果会的话,那么要删去第i-1个字符,然后在重新回到step1;
  • step1.2.2:如果不会的话,那么就保留第i-1位置的字符,之后出现的与第i-1字符相同的都不能再使用,因为他们构成的字符串字典序一定更大。

上面可能讲的有点乱,当应该可以发现存放结果的list每次增删都是在队尾操作,这很符合栈。

Language: java


class Solution {
    public String removeDuplicateLetters(String s) {
        char[] arr = s.toCharArray();
        int[] num = new int[26];
        for(char c : arr){
            num[c - 'a'] ++;//记录重复的个数
        }
        Deque<Character> stack = new ArrayDeque<>();
        int[] vis = new int[26];
        for(char c : arr){
            if(vis[c - 'a'] == 1) {
                num[c - 'a'] --;//当前字符被访问过了,不再访问
                continue;
            }
            if(stack.isEmpty() || stack.peek() < c){//当前字符比前一个栈顶字符来得大,符合较小字典序的规律,直接入栈
                stack.push(c);
                vis[c - 'a'] = 1;
                num[c - 'a'] --;
            }else{
                while(!stack.isEmpty() && stack.peek() > c && num[stack.peek() - 'a'] > 0){//当前字符比栈顶大,同时,栈顶字符在后面字符串中还会出现,那么就出栈
                    vis[stack.pop() - 'a'] = 0;
                }
                stack.push(c);
                vis[c - 'a'] = 1;
                num[c - 'a'] --;
            }
        }
        StringBuilder sb = new StringBuilder();
        while(!stack.isEmpty()){
            sb.append(stack.pollLast());
        }
        return sb.toString();
    }
}

posted @ 2020-12-20 17:52  yoyuLiu  阅读(176)  评论(0编辑  收藏  举报