给出一个由a-z组成的字符串S,求他的一个子序列,满足如下条件:
1、包含字符串中所有出现过的字符各1个。
2、是所有满足条件1的串中,字典序最小的。
例如:babbdcc,出现过的字符为:abcd,而包含abcd的所有子序列中,字典序最小的为abdc。
Input
输入1行字符串S,所有字符均为小写,字符串的长度为L。(1 <= L <= 100000)。
Output
输出包含S中所有出现过的字符,每个字符各1个,并且字典序最小的S的子序列。
Input示例
babbdcc
Output示例
abdc
题解:贪心,栈
分析:字母最多26个,数据范围可以不用太在意,因为是字典序,可以想象成从头开始的一个个试着加入一个数组中
思路:
第一步:栈为空,就直接加入一个元素
第二步:栈不为空,判断是否大于接下来要加入的元素,如果小于就直接加入(即字典序),否则判断一下后面是否还有栈顶元素
第三步:若还有栈顶元素,那么将栈顶 pop()出来,若后面没有了自然就要留在栈里面了
第四步:把接下来的这个元素放入栈中
演示题目样例:
b 空栈直接加入
a 因为后面还有b,所以pop原有的b,放入a
ab 加入b
ab 已经有b了就不能再加了
abd 加入d
abdc 加入c
为什么这个贪心是正确的呢?
解:因为题目要的是字典序最小,如果要的是逆序数最小那就不正确了
这个解法一定保证了:贪心的最初的字母尽可能最小,依次类推即可得到答案
#include<stack>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int num[50];
bool vis[50];
char a[100010];
stack<int>st;
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%s",a)!=EOF)
{
while(!st.empty())
st.pop();
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
int len=strlen(a),temp;
for(int i=0;i<len;i++){
temp=a[i]-'a';
num[temp]++;
}
for(int i=0;i<len;i++){
temp=a[i]-'a';
if(vis[temp]){///已在栈里面了
num[temp]--;
continue;
}
if(st.empty()){
st.push(temp);
vis[temp]=1;
num[temp]--;
}
else{
while(!st.empty()&&st.top()>temp)
{
if(num[st.top()])
vis[st.top()]=0,st.pop();
else
break;
}
st.push(temp);
vis[temp]=1;
num[temp]--;
}
}
int pos=0;
char ans[50];
while(!st.empty())
{
ans[pos++]=st.top()+'a';
st.pop();
}
for(int i=pos-1;i>=0;i--)
printf("%c",ans[i]);
puts("");
}
return 0;
}