316. Remove Duplicate Letters

72 阅读1分钟

image.png

题目要求

  • 去除字符串中的重复字母;
  • 不打乱相对位置;
  • 输出字典序最小。

方法

  • 遍历字符串元素并维护一个栈
  • 当准备入栈的元素比栈顶大的时候顺利压入
  • 当准备压入栈的元素比栈顶小的时候将栈顶元素弹出直至满足比栈顶元素大
    • 当准备弹出栈的元素在后续的字符串遍历中无法再出现(该元素的最后一个幸存者)则拒绝弹出,元素压入,遍历继续
  • 对被压入的元素做记录,在后续遍历中若该元素已经压入过了,则跳过
class Solution {
    public String removeDuplicateLetters(String s) {
        char[] arr = s.toCharArray();

        Stack<Character> stack = new Stack<>();

        // 记录字符最后一次出现的位置
        Map<Character, Integer> map = new HashMap<>();        
        for (int i = 0; i < arr.length; i++) {
            map.put(arr[i], i);
        }

        // 维护被记录在栈里的字符,表示访问过了
        Set<Character> set = new HashSet<>(); 

        for (int i = 0; i < arr.length; i++) {
            if (set.contains(arr[i])) {
                continue;
            }
            // 当前字符 小于 栈顶字符, 开始pop栈
            if (!stack.isEmpty() && arr[i] < stack.peek()) {
                // 保证当前pop的字符,后面还会出现,用last occur来判断
                while (!stack.isEmpty() 
                && arr[i] < stack.peek() && map.get(stack.peek()) > i) {
                    set.remove(stack.pop()); // pop + reomve
                }
            }
            stack.push(arr[i]);
            set.add(arr[i]);
        }

        // 构造字符串
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.reverse().toString();
    }
}