241008-316-去除重复字母同1081

79 阅读1分钟

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

示例 1:

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

示例 2:

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

思路

题目想要的是一个字典序最小,即尽量由小到大排列的字符串;
那么对于一个元素:
1)如果前面已经出现过该元素,因为该元素符合多个的条件2),所以他的后继元素必然大于该元素,即选用该位置相比前面的位置字典序必然更大,所以直接去掉 2)排查前一个字符,如果前一个字符>该字符且后面还会出现,那就可以去掉前一个元素让字典序更小。如果前一个字符只会出现一次,那就不能去掉,直接加入该元素到结果集
因为比较的是和pop的最后一个加入结果集的元素,符合后进先出,所以用stack结构

public String removeDuplicateLetters(String s) {
    //值小的要排在前面
    char[] arr = new char[26];
    boolean[] used = new boolean[26];
    for (int i = 0; i < s.length(); i++) {
        arr[s.charAt(i) - 'a']++;
    }
    Deque stack = new ArrayDeque<Character>();

    for (int i = 0; i < s.length(); i++) {
        arr[s.charAt(i) - 'a']--;
        //如果已经放入过直接跳,因为该位置肯定比之前出现的位置靠后,字典序最好也只是和上一个位置相等
        if (used[s.charAt(i) - 'a']){
            continue;
        }
        //如果前面的字符比该字符大且后面还会出现那就pop掉
        while (!stack.isEmpty() && arr[(char)stack.peek() - 'a'] > 0 && (char)stack.peek() > s.charAt(i)){
            used[(char)stack.peek() - 'a'] = false;
            stack.pop();
        }
        stack.push(s.charAt(i));
        used[s.charAt(i) - 'a'] = true;
    }
    StringBuffer res = new StringBuffer();
    while(!stack.isEmpty()){
        res.insert(0, stack.pop());
    }
    return res.toString();
}