316. 去除重复字母

难度中等

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

示例 1:

输入:s = "bcabc"

输出:"abc"

示例 2:

输入:s = "cbacdcbc"

输出:"acdb"

提示:

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

# 贪心 + 单调栈

例如:bcabcbcabc

i=0i =0bb 已经是满足字典序最小

i=1i=1bcbc 已经是满足字典序最小

i=2i=2:发现 c>ac > a

  • aa 后面有 cc,那么应该是 acac 而不是 caca。即:移除 cc,还剩余 bb
  • 此时 b>ab > aaa 后面有 bb,那么应该是 abab 而不是 baba。即:移除 bb
  • 最后的结果集:aa

i=3i=3abab 已经是满足字典序最小

i=4i=4abcabc 已经是满足字典序最小


例如:cbacdcbccbacdcbc

i=0i=0cc 已经是满足字典序最小

i=1i=1c>bc>bbb 后面含有 cc,那么应该是 bcbc 而不是 cbcb。即:移除 cc,将 bb 放入结果集:bb

i=2i=2b>ab > aaa 后面含有 bb,那么应该是 abab 而不是 baba。即:移除 bb,将 aa 放入结果集:aa

i=3i=3acac 已经是满足字典序最小

i=4i=4acdacd 已经是满足字典序最小

i=5i=5:结果集中含有 cc,可以贪心的选择前面的,排除当前元素

  • acd>adcacd > adc,选择后者则会导致结果集变大

i=6i=6d>bd > bbb 后面没有元素 dd,那么放入结果集:acdbacdb

i=7i=7:结果集中含有 cc,可以贪心的选择前面的,排除当前元素

最后的结果集:acdbacdb

枚举下标 ii

  • 若栈中元素包含 s[i]s[i],那么舍弃 s[i]s[i],因为若舍弃栈中元素,则会导致字典序更大
  • 若栈顶元素大于 s[i]s[i],若 ii 后面含有栈顶元素,那么移除栈顶元素,不断地重复此过程直至栈顶元素 <s[i]< s[i] 或者 ii 后面不包含栈顶元素,最后将 s[i]s[i] 入栈形成字典序最小
  • 若栈顶元素小于 s[i]s[i],那么此时已经满足字典序最小,将 s[i]s[i] 入栈

idx[i]idx[i] 为元素 ii 后面的重复元素的最远下标,采用 visited[i]visited[i] 数组记录栈中是否包含元素 ii

class Solution {
    public String removeDuplicateLetters(String s) {
        int n = s.length();
        //i 后面的重复元素的最远下标
        int[] arr = new int[26];
        for (int i = 0; i < n; i++) {
            arr[s.charAt(i) - 'a'] = i;
        }
        // 单调栈
        StringBuilder st = new StringBuilder();
        boolean[] visited = new boolean[26];
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            int v = ch - 'a';
            if (visited[v]) {
                continue;
            }
            int peek;
            // 栈顶元素大于当前元素 && s [i] 后面包含栈顶元素
            while (st.length() > 0 && (peek = st.charAt(st.length() - 1) - 'a') > v && arr[peek] > i) {
                st.deleteCharAt(st.length() - 1);
                visited[peek] = false;
            }
            visited[v] = true;
            st.append(ch);
        }
        return st.toString();
    }
}