greedy LeetCode 316. Remove Duplicate Letters

这周主题依旧是贪心算法,按照我们依旧省时省力的做法,找了一道难度为hard并且通过率在没有做过的里面最高的,这涉及一个桶排序,感兴趣的小伙伴可以自己查查要怎么做。这次我们选到的题目是316. Remove Duplicate Letters ,下面是题目:

--------------------这里是题目-------------------------

Given a string which contains only lowercase letters, remove duplicate letters so that every letter appear once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.

Example:

Given "bcabc"
Return "abc"

Given "cbacdcbc"
Return "acdb"

-----------------这里是题解--------------------------

题目大意很简单,给你一个全小写字母的字符串,你需要找到一个字典序最小并且无重复字母的子序列,最开始我以为这个字典序是最小的字典序,然后我就屁颠屁颠地找到了所有出现过的字母,然后排了一个序,结果错了,后来仔细想想再看看例子才知道这个字典序最小是基于原始字符串的序列的,就是说你的答案序列是原始序列的子序列,不变顺序的那种。

既然说了是贪心算法,那么主要的问题就是要找到一个完美的策略,来保证每一步的正确性,我们的思路很简单,我们要怎么保证我们找到的第一个字母就是答案的第一个字母,我们永远要找当前子序列中的所求的第一个字母,这样我们就能保证最后的答案是字典序最小。

思考一下,如果在cbacdcbc这个序列中,我们要找答案序列中的第一个字母a,a在答案和在原始序列中的位置有什么特点呢?比a优先级低的字母在a后面都有再出现,那么有一个问题,在满足前面这个条件下出现了多个a你要怎么选?当然是选第一次出现的那个啊,那么量化一下我们就可以得出一个完美的策略了。一个字母想要成为当前子序列中答案中的第一字母的话,它第一次出现的下标需要小于其他所有字母出现的最后一次的下标,重复直到所有的字母都出现在我们的答案中、由于我们是从a-z的顺序遍历的,所以我们是可以得到这么一个正确的结果的。

策略就是这样了,接下来就是代码实现了

---------------------下面是代码---------------------

#include <iostream>
#include <string>
#include <memory.h>
#include <algorithm>
#include <queue> 
#include <vector>
using namespace std;

class Solution {
public:
    static string removeDuplicateLetters(string s) {
        queue<int> ain[26];//用来存储每个字母出现的所有下标,由于我们从左到右遍历,所以可以保证队列是递增的 
		string ans="";
        int mi[26];//储存每个字母出现的最后一次下标 
		for(int i=0;i<s.length() ;i++)
		{
			int index=s[i]-'a';
			ain[index].push(i);
		}	
		for(int i=0;i<26;i++)
		{
			if(!ain[i].empty())
			mi[i]=ain[i].back();
			else
			mi[i]=100000;
		}
		while(1)
		{	
			int m=100000;
			for(int i=0;i<26;i++)//找到字母出现最后一次下标最小的一个 
			if(mi[i]<m)
			m=mi[i]; 
			if(m==100000)//表明所有字母都添加到了我们答案字符串中 
			break;
			for(int i=0;i<26;i++)
			{
				if(ain[i].empty()) 
				continue;
				int k=ain[i].front();
				if(k<=m)
				{
					while(!ain[i].empty())//这个字母已经确定了,将队列清空相当于消除重复的字母 
					ain[i].pop();
					for(int p=0;p<26;p++)
					{
						if(!ain[p].empty())
						while(ain[p].front()<k)//将下边k左边的所有字母清空,因为k已经是最左边的一个元素了 
						ain[p].pop();
					}
					mi[i]=100000;
					ans+=static_cast<char>('a'+i);
					break;
				}
			}
		}
		return ans;
    }
};

策略有了之后代码实现起来还是简单的,毕竟程序最重要的是算法,最难的也是算法,实现都是其次的,最开始我还在担心会不会超时,最后答案表明这道题没有上次那道贪心,用了3ms,甚至高于了平均水平。

------------------------再见的分割线-----------------------

see you next illusion



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值