LeetCode 316. 去除重复字母--贪心策略+单调栈

  1. 去除重复字母

给你一个字符串 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:s=“bcabc”,s[1:2]=“ca”,因为a<c并且c在后面的位置还出现过,所以可以删除这个c替换为a,这样肯定保证了得到的字符串字典序最小,并且每个字符出现的次数唯一。

于是问题变成了,怎么去高效的处理并且判断样例1中的"ca"情况,不难发现这样相邻的两个字符有这样的特点,一个是ans[s[i]]>0,表示后面还有字符s[i]出现,同时s[i]>s[i+1],同时如果s[i+1]之前已经被保留下来,就不能再考虑,因为按这样的处理方式,之前的字符串保留下来的字符肯定是一种比较优化的情况,而且字符只能出现一次,好比:“bacdegacdg”,s[1]=‘a’,s[6]=‘a’,但是s[6]去替换s[5]意义不大,因为后面的字符也会继续替换,导致的结果等同于s[6]没有参与替换,所以这也有一个要求,如果之前已经保留下来的字符,后面再出现不能再考虑。

于是我们需要一个数组ans[maxn]标记每个字符出现的次数,再用数组vis标记每个字符之前是否存在过。

然后针对"bcabc"这样的情况,a可以替换掉c,然后再替换掉b,是一个需要连续判断的过程,于是我们想到栈来储存字符,这个就类似于单调栈的特性。

AC代码

class Solution {
public:
    stack<char>q;
    int ans[30];//标记所有字符出现的次数
    bool vis[30];//标记已经保留下来的字符,那么后面遇到已经出现的字符就不能再保留
    string removeDuplicateLetters(string s) {
        memset(ans,0,sizeof(ans));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<s.length();i++)
        ans[s[i]-'a']++;
        for(int i=0;i<s.length();i++)
        {
            ans[s[i]-'a']--;//字符s[i]减少1次
            if(q.empty())
            {
                vis[s[i]-'a']=true;//加入栈
                q.push(s[i]);
            }
            else if(vis[s[i]-'a']==false)
            {
                //ans[q.top()-'a']>0表示后面还有这样的字符存在,可以删除替换
                while(q.empty()==false&&ans[q.top()-'a']>0&&q.top()>s[i])
                {
                    vis[q.top()-'a']=false;从栈里弹出
                    q.pop();
                }
                vis[s[i]-'a']=true;//加入了栈
                q.push(s[i]);
            }
            
        }
        string t="";
        while(!q.empty())
        {
            t+=q.top();
            q.pop();
        }
        reverse(t.begin(),t.end());
        return t;
    }
};

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值