字典序最小的子序列
给出一个由a-z组成的字符串S,求他的一个子序列,满足如下条件:
1、包含字符串中所有出现过的字符各1个。
2、是所有满足条件1的串中,字典序最小的。
例如:babbdcc,出现过的字符为:abcd,而包含abcd的所有子序列中,字典序最小的为abdc。
输入
输入1行字符串S,所有字符均为小写,字符串的长度为L。(1 <= L <= 100000)。
输出
输出包含S中所有出现过的字符,每个字符各1个,并且字典序最小的S的子序列。
输入样例
babbdcc
输出样例
abdc
链接:
http://www.51nod.com/Challenge/Problem.html#!#problemId=1255
思路:
贪心。可以用栈来存答案,首先把每个字符出现的最后位置记录下来。然后从左到右开始找,如果栈顶元素大于现在元素并且栈顶元素在后面还有(即栈顶元素的最后位置>i),那么出栈,取消标记(这里用while循环,有可能栈中有好几个元素需要出栈)。没有标记的直接进栈,标记。最后倒序输出。。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int last[maxn];
bool vis[maxn];
int main()
{
string p;
cin >> p;
int len = p.length();
stack<int > s;
while(!s.empty()){
s.pop();
}
for(int i = 0; i < len; i++) {
int k = p[i];
if(last[k] < i)
last[k] = i;
}
for(int i = 0; i < len; i++) {
int k = p[i];
if(vis[k]) { //别忘判断是否被标记,标记直接跳过即可。
continue;
}
while(!s.empty() && last[s.top()] > i && k <= s.top()) {
vis[s.top()] = 0;
s.pop();
}
if(!vis[k]) {
vis[k] = 1;
s.push(k);
}
}
int ans[maxn];
int tot = 0;
while(!s.empty())
{
ans[tot++] = s.top();
s.pop();
}
for(int i = tot-1; i >= 0; i--) {
printf("%c", ans[i]);
}
return 0;
}