去除重复字母

题目描述

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

示例 1:
输入:s = “bcabc”
输出:“abc”

示例 2:
输入:s = “cbacdcbc”
输出:“acdb”

提示:
1 <= s.length <= 104
s 由小写英文字母组成

解题思路

class Solution {
public:
    string removeDuplicateLetters(string s) {
        unordered_map<char, int> count;
        unordered_set<char> inStack;
        stack<char> stk;
        for (auto c: s) {
            count[c]++;
        }
        for (auto c: s) {
            count[c]--;
            if (inStack.count(c)) {
                continue;
            }
            while (!stk.empty() && c < stk.top() && count[stk.top()]) {
                inStack.erase(stk.top());
                stk.pop();
            }
            stk.push(c);
            inStack.insert(c);
        }
        string ans;
        while (!stk.empty()) {
            ans += stk.top();
            stk.pop();
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

代码分析

class Solution {
public:
    string removeDuplicateLetters(string s) {
        unordered_map<char, int> count;  // 存储每个字符的出现次数
        unordered_set<char> inStack;  // 记录栈中已有的字符,快速检查一个字符是否在栈中
        stack<char> stk;  // 用来存储最终结果的字符,确保字符的顺序和唯一性
  • 计数字符频率
    • 使用 unordered_map 来记录每个字符在字符串 s 中出现的次数。
    • 遍历字符串 s,为每个字符的出现次数计数。
        for (auto c: s) {
            count[c]++;
        }
  • 遍历字符串进行处理
    • 再次遍历字符串 s,对于每个字符 c,首先减少其在 count 中的计数(表示这个字符被处理过一次)。
    • 如果字符已在栈中(通过 unordered_set 检查),则跳过当前字符,因为我们要保证每个字符在结果中只出现一次。
        for (auto c: s) {
            count[c]--;
            if (inStack.count(c)) {
                continue;
            }
  • 栈操作保证字典序
    • 检查当前栈是否为空,栈顶字符是否大于当前字符 c,并且栈顶字符在后面还会出现(通过 count[stk.top()] > 0 检查)。
    • 如果以上条件都满足,弹出栈顶字符,并在 inStack 中移除该字符。
    • 将当前字符 c 压入栈,并在 inStack 中记录。
            while (!stk.empty() && c < stk.top() && count[stk.top()]) {
                inStack.erase(stk.top());
                stk.pop();
            }
            stk.push(c);
            inStack.insert(c);
        }
  • 构建最终结果字符串
    • 从栈中弹出所有字符并添加到结果字符串 ans
    • 由于栈是后进先出的,我们需要反转字符串 ans 以获得正确的顺序。
        string ans;
        while (!stk.empty()) {
            ans += stk.top();
            stk.pop();
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(N),其中 N 是字符串 s 的长度。每个字符最多被推入和弹出栈一次。
  • 空间复杂度:O(1),虽然使用了额外的数据结构,但由于输入限制为小写字母,因此空间使用量被限制在常数级别。

结论

这个算法利用了栈的特性和贪心的思想,高效地解决了移除重复字符并保证字典序最小的问题。使用栈来维护字符顺序和 unordered_set 快速查找已处理的字符是此解法的关键。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LIHAORAN99

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值