字典之序
题目链接:ybtoj高效进阶 21184
题目大意
给你一个字符串,要你找到一个字典序最小的子序列,使得每个在原串中有的字符都在这个串中出现且只出现一次。
思路
首先我们肯定是把能有的字符都找出来。
然后肯定就是贪嘛,每次从没有放的中选一个最小的可以放的放。
那问题就变成怎么判断是否可以放。
然后显然是首先要有这个字符在当前的位置后面,而且你其它没有放过的字符在后面都要出现过。
然后你直接暴力扫过去判断就可以了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[50001], kd[50001];
int n, m, l, dy[50001];
bool use[31];
bool see[31];
bool work(char c) {
int noww = l;
while (noww <= n && s[noww] != c) noww++;
if (noww > n) return 0;
memset(see, 0, sizeof(see));
see[dy[noww]] = 1;
for (int i = noww + 1; i <= n; i++) {
if (!see[dy[i]]) {
see[dy[i]] = 1;
}
}
for (int i = 1; i <= m; i++)
if (!see[i] && !use[i]) return 0;
return 1;
}
int main() {
// freopen("aorder.in", "r", stdin);
// freopen("aorder.out", "w", stdout);
scanf("%s", s + 1);
n = strlen(s + 1);
memcpy(kd, s, sizeof(kd));
sort(kd + 1, kd + n + 1);
m = unique(kd + 1, kd + n + 1) - kd - 1;
for (int i = 1; i <= n; i++) dy[i] = lower_bound(kd + 1, kd + m + 1, s[i]) - kd;
l = 1;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= m; j++)
if (!use[j]) {
if (work(kd[j])) {
use[j] = 1;
putchar(kd[j]);
while (s[l] != kd[j]) l++;
l++;
break;
}
}
}
return 0;
}