Leetcode.316 去除重复字母

题目链接

Leetcode.316 去除重复字母 mid

题目描述

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

示例 1:

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

示例 2:

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

提示:
  • 1 < = s . l e n g t h < = 1 0 4 1 <= s.length <= 10^4 1<=s.length<=104
  • s 由小写英文字母组成

解法:单调栈 & 贪心

首先我们考虑要怎么删除字符,才能使剩下的字符串字典序最小?

我们要删除第一个 s [ i ] > s [ i + 1 ] s[i] > s[i + 1] s[i]>s[i+1] s [ i ] s[i] s[i],这样的话剩下的字符串字典序就最小。

我们记作这样的字符为 有效字符,我们要删除的就是这样的有效字符。

所以我们可以使用一个单调栈 s t k stk stk,从栈底 到 栈顶是递增的。假设 区间 [ 0 , i − 1 ] [0,i - 1] [0,i1] 都已经被我们处理了,现在处理 s [ i ] s[i] s[i]

如果 ! s t k . e m p t y ( ) & & s t k . t o p ( ) > s [ i ] !stk.empty() \&\&stk.top() > s[i] !stk.empty()&&stk.top()>s[i],说明此时的 s t k . t o p ( ) 和 s [ i ] stk.top() 和 s[i] stk.top()s[i]就是有效字符,需要删除 s t k . t o p ( ) stk.top() stk.top(),即 s t k stk stk弹出栈顶元素。

由于题目保证每一个字符只能出现一次,所以我们需要两个哈希表 c n t 和 v i s cnt 和 vis cntvis v i s vis vis 记录当前字符 s [ i ] s[i] s[i] 是否已经在栈内,如果已经在栈内,就跳过;否则才进行处理。

c n t cnt cnt 初始记录 s s s 中所有字符的出现次数。每遍历过 s [ i ] s[i] s[i], c n t [ s [ i ] ] = c n t [ s [ i ] ] − 1 cnt[s[i]] = cnt[s[i]] - 1 cnt[s[i]]=cnt[s[i]]1。它的作用是,当 ! s t k . e m p t y ( ) & & s t k . t o p ( ) > s [ i ] !stk.empty() \&\&stk.top() > s[i] !stk.empty()&&stk.top()>s[i],栈顶元素要出栈,但是如果 剩下的未遍历的区间已经不存在字符 s t k . t o p ( ) stk.top() stk.top(),也就是判断 c n t [ s t k . t o p ( ) ] cnt[stk.top()] cnt[stk.top()] 是否大于0。如果不存在了,就不能出栈;否则可以。

时间复杂度: O ( n ) O(n) O(n)

C++代码:


class Solution {
public:
    string removeDuplicateLetters(string s) {
        unordered_map<char,int> cnt,vis;
        for(auto c:s) cnt[c]++;

        string stk;

        for(auto ch:s){
            if(!vis[ch]){
                while(!stk.empty() && stk.back() > ch){
                    if(cnt[stk.back()]){
                        vis[stk.back()] = 0;
                        stk.pop_back();
                    }
                    else break;
                }
                stk.push_back(ch);
                vis[ch] = 1;
            }
            cnt[ch]--;
        }
        return stk;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值