G. Maximize the Remaining String

G. Maximize the Remaining String

题意

给定一个字符串 如果某个字符在字符串中出现了超过两次 就删掉其中一个字符 直到最后字符串中包含的字符都只出现一次为止
求操作后最大字典序字符串 (若某个字符串是另一个字符串的前缀 那么规定该字符串比另一个字符串大)

思路

首先用一个cnt数组 记录原字符串中每个字符的个数
然后用栈模拟删除操作
从头遍历字符串 每次和栈头元素比较
如果栈头元素更小且剩余的个数超过两个就把栈头元素删去 对应字符的个数减少
每次操作后都放进当前字符
如果当前元素已经在栈中存在 无论它跟栈顶字符大小如何 直接跳过(删除)当前字符就好了
因为当前元素如果已在栈中没被删 说明它要存在前面来当做约束 使得前面更优,因为前面越大才能使得整个字符串越大故删掉当前这个字符是最优的选择
最后栈中的字符串就是答案

#include <bits/stdc++.h>
#include <stdlib.h>
using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define ll long long
#define int ll
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 5;
const int M = 5e5 + 5;
const ll mod = 998244353;

int n, m;
int cnt[300], cnt2[300];
void solve()
{
	string s;
	cin >> s;
	n = s.size();

	memset(cnt, 0, sizeof cnt);
	memset(cnt2, 0, sizeof cnt2);
	for(int i = 0; i < n; i++)
	    cnt[s[i]]++;
	
	stack<int>st;
	for(int i = 0; i < n; i++){
		if(cnt2[s[i]]) {
			cnt[s[i]]--;
			continue;
		}
		while(st.size()){
			int now = st.top();
			if(cnt[now] > 1 && now <= s[i]){
				st.pop();
				cnt[now]--;
				cnt2[now]--;
			}
			else break;
		}

		st.push(s[i]);
		cnt2[s[i]]++;
	}
	
	string ans = "";
	while(st.size()){
		ans += st.top();
		st.pop();
	}
	for(int i = ans.size() - 1; i >= 0; i--)
		cout << ans[i];
	cout << "\n";
}


signed main()
{
	IOS;
	int t = 1;

	cin >> t;
	while (t--)
	{
		solve();
	}
}
posted @ 2023-01-02 11:26  Yaqu  阅读(33)  评论(0编辑  收藏  举报