316. 去除重复字母

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Fudanqqqqq/article/details/102104125

Q:

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

示例 1:

输入: “bcabc”
输出: “abc”
示例 2:

输入: “cbacdcbc”
输出: “acdb”

A:

自己不会,看的别人思路,构造一个栈,遍历字符串,开一个数组做字典记录当前已有的字母。首先若当前字母已记录则continue。若当前字母大于栈顶字母则放入,否则若在当前字母后面的子串中还有栈顶字母的话则pop,这里就是贪心。因为假如当前栈里是d,f,g,当前字母为a,即字符串中第一个a就是当前这个a,故目前的最优解就是包含这个a(因为题目要求所有存在的字母必须出现一次)。而题目又要求字典序最小,想象一个单词中有字母a,显然这个a越靠前越好,abcd和bacd、bcad、bcda显然abcd字典序最小是吧。所以我们就尽量把a往栈底移动,但要满足pop掉的栈顶字母在后面还存在副本,不然不满足题目的所有字母出现一次的要求了。另外这里有些同学可能会考虑之前栈是bcd,当前字母是a,a后面的子串比如是dcb,即当前栈的字典序(bcd)比当前字母后面的子串对应的序列(dcb)小的情况。但事实是能pop掉的栈顶,元素都比当前元素小,故最终字典序是一定更小的(即一定是更优解)。类比1、2、3三个数字怎么排列最大,显然213<312。虽然12比21小,但只要我把3移到最前面,整个数字还是更大,不知道这么说能不能说明白。

#include<set>
#include<map>
#include<regex>
#include<sstream>
#include<iostream>
#include<stdio.h>
#include<list>
#include<cstdlib>
#include<stack>
using namespace std;
//19/10/4
class Solution {
public:
	string removeDuplicateLetters(string s)
	{
		stack<char> sta;
		int last_exist[26] = { 0 };
		for (int i = 0; i < s.size(); ++i)
		{
			last_exist[s[i] - 'a'] = i;
		}
		int visited[26] = { 0 };
		for (int i=0;i<s.size();++i)
		{
			char c = s[i];
			if (visited[c - 'a'])
			{
				continue;
			}
			if (sta.empty() or c>sta.top())
			{
				sta.push(c);
			}
			else
			{
				string cur_s = s.substr(i + 1, s.size());
				while (!sta.empty() and sta.top() > c and last_exist[sta.top()-'a']>i)
				{
					visited[sta.top() - 'a'] = 0;
					sta.pop();
				}
				sta.push(c);
			}
			visited[c - 'a'] = 1;
		}
		string res = "";
		while (!sta.empty())
		{
			res += sta.top();
			sta.pop();
		}
		reverse(res.begin(), res.end());
		return res;
	}
};
int main()
{
	Solution x;
	string s = "cbacdcbc";
	cout<<x.removeDuplicateLetters(s);
	getchar();
}

猜你喜欢

转载自blog.csdn.net/Fudanqqqqq/article/details/102104125