LeetCode 316. 去除重复字母

316. 去除重复字母

题目描述

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

示例1

输入:s = “bcabc”
输出:“abc”

示例2

输入:s = “cbacdcbc”
输出:“acdb”

解题思路

思路一: 栈

题目要求有三点:

  1. 去重;
  2. 去重字符串中的字符顺序不能打乱s中字符出现的相对顺序;
  3. 结果为字典序最小的字符串。

先来实现要求1、2, 这里我们使用「栈」这种数据结构来实现:
实现代码如下:

/**
 * @param {string} s
 * @return {string}
 */
var removeDuplicateLetters = function(s) {
    // 存放去重的结果
    let stack = [], 
        // 记录栈中是否存在某个字符
        vis = {};
    for (let c of s) {
        // 如果字符 c 存在栈中,直接跳过
        if (vis[c]) continue;
        // 若不存在,则插入栈顶并标记为存在
        stack.push(c);
        vis[c] = true;
    }
    return stack.join('');
};

这段代码基本上满足需求1、2, 但怎么要保证字典序最小呢?
我们在向栈stack中插入字符时, 我们插入一个while循环,pop栈顶字符与当前字符进行比较, 直到栈顶元素比当前元素的字典序还小为止。
此时,还有个问题需要解决: 如果pop出的栈顶元素,且后续字符没有该字符如何处理?
例: s=‘bcabc’, 在插入’a’时,发现前面的字符’c’字典序要大,且在 ‘a’ 之后还存在字符 ‘c’,那么栈顶的这个 ‘c’ 就会被 pop 掉。

while 循环继续判断,发现前面的字符 ‘b’ 的字典序还是比 ‘a’ 大,但是在 ‘a’ 之后再没有字符 ‘b’ 了,所以不应该把 ‘b’ pop 出去。
此时我们可以维护一个计算器记录字符串中每个字符的数量,如果次数大于1, 则可以pop。 这样就可以解决这个问题。

实现代码如下:

/**
 * @param {string} s
 * @return {string}
 */
var removeDuplicateLetters = function(s) {
    let stack = [], 
        vis = {}, 
        count = {};
    for (let c of s) {
        if (count[c]) {
            count[c]++;
        } else {
            count[c] = 1;
        }
        
    }
    for (let c of s) {
        // 每遍历过一个字符,都将对应的计数减一
        count[c]--;
        if (vis[c]) continue;
        
        while (stack.length && stack[stack.length - 1] > c) {
            // 若之后不存在栈顶元素了,则停止 pop
            if (count[stack[stack.length - 1]] == 0) {
                break;
            }
            // 若之后还有,则可以 pop
            vis[stack.pop()] = false;
        }
        stack.push(c);
        vis[c] = true; 
    }
    return stack.join('');
};
  • 时间复杂度: O(n); 其中 n 为字符串长度, 代码中虽然有双重循环,但是每个字符至多只会入栈、出栈各一次。
  • 空间复杂度: O(n);

参考资料

https://leetcode.cn/problems/remove-duplicate-letters/solution/you-qian-ru-shen-dan-diao-zhan-si-lu-qu-chu-zhong-/s

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值