- 去除重复字母
给你一个字符串 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;
}
};