https://www.51nod.com/Challenge/Problem.html#!#problemId=1255

给出一个由a-z组成的字符串S,求他的一个子序列,满足如下条件:

1、包含字符串中所有出现过的字符各1个。
2、是所有满足条件1的串中,字典序最小的。

例如:babbdcc,出现过的字符为:abcd,而包含abcd的所有子序列中,字典序最小的为abdc。
输入
输入1行字符串S,所有字符均为小写,字符串的长度为L。(1 <= L <= 100000)。
输出
输出包含S中所有出现过的字符,每个字符各1个,并且字典序最小的S的子序列。
输入样例
babbdcc
输出样例
abdc

从前往后遍历字符串,维护一个栈。如果当前字符串 c 小于栈顶且不在栈内,且栈顶字符在后面还有出现,则弹栈并将 c 入栈,依次循环直到不符合条件。
如果当前字符已经在栈内,则跳过。
如果 c 大于栈顶,且不在栈内,则入栈,否则跳过。
用两个数组维护字符还剩余的个数 和 是否已在栈内。
总结:入栈两种情况(+第一个字符), 出栈一种,其余的跳过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <cstdio>
#include <cstring>
#include <set>
#include <iostream>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define MAX 100000+10
#define INF 0x3f3f3f3f
char a[MAX];
int t[150],flag[150];
char s[27],c;
int h=0,l;
int main(){
freopen("in","r",stdin);
int k=-1;
while((c=getchar())!='\n'){
a[++k]=c;
t[c]++;
}
a[++k]='\0'; s[0]=150;
l=strlen(a); //strlen () 写在循环里会超时
for(int i=0;i<l;i++){
if(!h){
s[++h]=a[i];
flag[a[i]]=1;
}
if(flag[a[i]]){
t[a[i]]--;
continue;
}
if( (a[i]<s[h] && !t[s[h]]) || a[i]>s[h] ){
s[++h]=a[i];
flag[a[i]]=1;
}
else if(a[i]<s[h] && t[s[h]] ){
flag[s[h]]=0;
s[h]=a[i];
flag[a[i]]=1;

while(a[i]<s[h-1] && t[s[h-1]]){
h--;
flag[s[h]]=0;
s[h]=a[i];
}
}
t[a[i]]--;
}
s[++h]='\0';
puts(s+1);
return 0;
}