LeetCode/solutions/316. Remove Duplicate Letters.md
2022-11-01 23:47:24 +08:00

2.2 KiB
Raw Blame History

316. Remove Duplicate Letters

思路

题目有两个要求1每个字符只出现一次2字典序最小。要求1比较好办记录下每个字符出现的次数如果出现过就去掉即可。为了满足条件2需要尽可能把小字符放在前面,所以需要找到满足 s[i]>s[i+1] 且 后面还存在字符 s[i] 的下标 i然后去掉 s[i] 。

在理解这点之后,一个直观的题解思路是:对于字符串 s从左往右遍历找到满足条件的 s[i],去除它后得到新的字符串 s然后不断进行这样的循环直到不存在重复字符。但是这种解法会创建大量的中间字符串复杂度较高。由于在删除s[i]之后下一个需要被删除的字符串只可能在之后所以我们其实只需要遍历一次即可找到所有s[i]并将其删除,类似与单调栈,只是有一些限制条件(因为需要保留所有出现过的字符)。

为此我们首先需要先遍历一遍s统计每个字符的剩余出现次数。然后维护一个初始为空的最终结果字符串stk并遍历一遍s 1对于当前字符 c如果已经在stk中说明应该去掉c因为有更靠前的c 2否则如果stk末尾字符大于c且stk末尾字符在s后面还会出现那应该去掉stk末尾字符循环直至条件不成立然后将c加入stk

C++

class Solution {
public:
    string removeDuplicateLetters(string s) {
        if(s.size() <= 1) return s;

        vector<int>remain(256, 0);
        for(char c: s) remain[c]++;  // 标记字符c剩余多少个

        string stk;
        vector<bool>in_stk(256, false); // 是否已经在stk中
        for(char c: s){
            if(!in_stk[c]){  // 如果不在stk中
                while(!stk.empty() && stk.back() > c){  //如果c比栈顶字符小
                    if(remain[stk.back()] == 0) break;  // 如果后面没有栈顶字符,那么不应该pop
                    in_stk[stk.back()] = false;
                    stk.pop_back();
                }
                stk.push_back(c);
                in_stk[c] = true;
            }
            remain[c]--;
        }
        return stk;
    }
};