DAILY DOCDAILY DOC
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
  • Array

    • 数组
    • 二分查找
    • moveZeros
  • Dynamic-programming

    • 动态规划
  • 刷题指南
  • String

    • 字符串
  • bitwise-operator

    • 位运算符
  • heap
  • history

    • [1014] 最佳观光组合
    • [1022] 从根到叶的二进制数之和
    • [104] 二叉树的最大深度
    • [11] 盛最多水的容器
    • [110] 平衡二叉树
    • [1227] 飞机座位分配概率
    • [129] 求根节点到叶节点数字之和
    • [1306] 跳跃游戏 III
    • [148] 排序链表
    • 155.最小栈
    • [165] 比较版本号
    • 1763. 最长的美好子字符串
    • [1870] 准时到达的列车最小时速
    • [199] 二叉树的右视图
    • [21] 合并两个有序链表
    • 215.数组中的第 k 个最大元素
    • [2306] 公司命名
    • [234] 回文链表
    • [2516] 每种字符至少取 K 个
    • [316] 去除重复字母
    • [3171] 找到按位或最接近 K 的子数组
    • [322] 零钱兑换
    • [41] 缺失的第一个正数
    • [44] 通配符匹配
    • [494] 目标和
    • [509] 斐波那契数
    • [518] 零钱兑换 II
    • [62] 不同路径
    • [676] 实现一个魔法字典
    • 70 爬楼梯
    • [718] 最长重复子数组
    • [78] 子集
    • [82] 删除排序链表中的重复元素 II
    • [871] 最低加油次数
    • [88] 合并两个有序数组
    • [887] 鸡蛋掉落
    • 958.二叉树的完全性检验
    • [98] 验证二叉搜索树
    • [983] 最低票价
    • leetcode practice
    • 约瑟夫问题
    • 移除重复节点
  • linked-list

    • 706. 设计哈希映射
    • 链表
  • stack

    • stack
  • tree

    • Tree Traversal
    • 二叉树的最近公共祖先
    • 二叉树
    • 题目
  • leetCode 刷题
  • 回溯
  • 排序算法

[316] 去除重复字母

details

解题思路

为了保证字典序最小并且去重,我们可以使用贪心 + 单调栈来解决这个问题。核心思路如下:

  1. 贪心策略:我们想要尽可能保持字母的相对顺序,并且选择字典序小的字母优先出现在前面。
  2. 栈的使用:利用栈存储最终要保留的字符,确保栈中的字符保持字典序递增。
  3. 去重:我们需要确保每个字符只出现一次,因此使用一个布尔数组来记录字符是否在栈中。
  4. 维护后续字符的可用性:我们需要记录每个字符在字符串中最后一次出现的位置,以便在遇到当前字符时,判断它是否还能出现在后续的字符中。

算法步骤:

  1. 遍历字符串,对每个字符进行判断:
    • 如果当前字符已经在栈中,则跳过。
    • 如果当前字符小于栈顶字符,且栈顶字符在后续还有出现,则可以将栈顶字符弹出,以确保字典序最小。
  2. 将当前字符压入栈,并标记该字符已经使用。
  3. 最终栈中的字符就是去重后的、且字典序最小的结果。

JavaScript 实现

var removeDuplicateLetters = function (s) {
    const size = s.length;
    let stack = [];

    let lastIndexArr = new Array(size).fill(0);
    for (let index = 0; index < size; index++) {
        lastIndexArr[s[index].charCodeAt(0) - 97] = index;
    }

    const set = new Set();
    for (let i = 0; i < size; i++) {
        const offset = s[i].charCodeAt(0) - 97;

        if (set.has(offset)) continue;
        while (stack.length && stack.at(-1).charCodeAt(0) > s[i].charCodeAt(0) && lastIndexArr[stack.at(-1).charCodeAt(0) - 97] > i) {
            set.delete(stack.pop()?.charCodeAt(0) - 97);
        }
        set.add(offset);
        stack.push(s[i]);
    }
    return stack.join('');
};

复杂度分析

  • 时间复杂度: O(n),其中 n 是字符串的长度。我们只遍历字符串一次,且每个字符最多入栈和出栈各一次。
  • 空间复杂度: O(1),由于只使用了常数级别的辅助数组来记录字符信息(长度为 26 的固定数组)。
Last Updated:
Contributors: rosendo
Prev
[2516] 每种字符至少取 K 个
Next
[3171] 找到按位或最接近 K 的子数组