20.12.20 316. 去除重复字母 单调栈

题目

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

注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters 相同

示例 1:
输入:s = "bcabc"
输出:"abc"

示例 2:
输入:s = "cbacdcbc"
输出:"acdb"

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

思路

  1. 还是没有思路的一题,想了15分钟多一点,就去看题解了,看到题解题目的单调栈,就醍醐灌顶了
  2. 明明之前做过类似的,要去维持一个东西保持最小或最大就用单调栈,可惜没想到
  3. 知道是单调栈,基本就很容易解决了
  4. 先统计各个字母出现的次数,用map rec存
  5. 单调栈,维持最小字典序。当一个字母放入栈后,只有比它小的字母有机会将它弹出,比它大的不能;相同字母一定留着先放进去的那个,因为它第一次入栈呆的地方就是它能满足最小字典序条件前提下到达的最前的位置。所以用set visited统计字母是否已放入栈
  6. 若该字母之前没放入栈,①当当前栈顶字母比它大,②并且栈顶字母在该字母之后还存在剩余(查看rec),就弹栈,直到为空或不符合前面两个条件
  7. 弹栈结束后,把该字母压入栈中,并标记visited已入栈
  8. 减少该字母的剩余数量
  9. 遍历结束后把stack的值一一弹到ans即可

代码

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

        stack<char> monotonousStack;
        unordered_set<char> visited;
        for(auto c:s){
            if(visited.count(c) == 0){
                while(!monotonousStack.empty() && c < monotonousStack.top()
                && rec[monotonousStack.top()] > 0){
                    visited.erase(monotonousStack.top());
                    monotonousStack.pop();
                }
                
                monotonousStack.push(c);
                visited.insert(c);
            }
            --rec[c];
        }

        string ans(monotonousStack.size(), 'a');
        int i = monotonousStack.size()-1;
        while(!monotonousStack.empty()){
            ans[i--] = monotonousStack.top();
            monotonousStack.pop();
        }
        return ans;
    }
};
posted @ 2020-12-20 08:41  肥斯大只仔  阅读(109)  评论(0编辑  收藏  举报