【ybtoj高效进阶 21184】字典之序(贪心)(模拟)

字典之序

题目链接: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;
}

Guess you like

Origin blog.csdn.net/weixin_43346722/article/details/121341837