316. 去除重复字母
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例 1:
输入:s = “bcabc”
输出:“abc”
示例 2:
输入:s = “cbacdcbc”
输出:“acdb”
提示:
1 <= s.length <= 104
s 由小写英文字母组成
字典序
首先讲一讲这一题的字典序,字符串的字典序,就是从字符串的第一位开始比较,如果第一位ASCII小则字典序就小,如果第一位相同,则第二位的字典序谁小就小,以此类推。故这一题应该尽可能让字典序小的靠前。
单调栈
一直以为这一题是要最小字典序是用单调队列,但是谁知道是用单调栈。
首先要想到:单调递增的字符串就是最小的。
以bcabc为例,先将b推入栈,再将c推入栈,是递增的可以直接推,然后到a,而ca肯定是大于ac的,所以如果后面还有c的话会更小,就想到可以用数组记录各个字母的次数,再用一个数组记录字母是否在栈中,后面还有c,所以可以把c推出栈,再看b,同样的过程,也可以把b推出栈,再将b和c分别推入栈。
故过程是:b入,c入,c出,b出
后入先出,符合栈的特征。
代码
class Solution {
public:
string removeDuplicateLetters(string s)
{
vector<int>num(26),exi(26,0);
string sub="";
stack<char>stk;
for(int i=0;i<s.size();i++) num[s[i]-'a']++;
for(int i=0;i<s.size();i++)
{
if(exi[s[i]-'a']==1)
{
num[s[i]-'a']--;
continue;
}//栈中存在的就直接不考虑了,注意按照例子b刚开始就在栈中,
//那后面的b怎么办,后面会有个a而把前面的bpop出去的。
if(!stk.empty()&&s[i]>stk.top()&&exi[s[i]-'a']==0)
{
stk.push(s[i]);
exi[s[i]-'a']=1;
num[s[i]-'a']--;
}
else
{
while(!stk.empty()&&s[i]<stk.top())
{
if(num[stk.top()-'a']>0)
{
exi[stk.top()-'a']=0;
stk.pop();
}
else break;
//这个必不可少,不然如果满足while不满足if就一直循环出不去了
}
if(exi[s[i]-'a']==0)
{
stk.push(s[i]);
num[stk.top()-'a']--;
exi[stk.top()-'a']=1;
}
}
}
while(stk.size()!=0)
{
sub=stk.top()+sub;
stk.pop();
}
s=sub;
return s;
}
};