版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
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();
}