C. Decreasing String Educational Codeforces Round 156 (Rated for Div. 2)

Problem - C - Codeforces

题目大意:给出一个字符串s[1],和一个数n,每次操作删除一个字母使s[i]字典序最小,得到新字符串s[i-1],直到得到长度为1的s[i]后,将s[i]字符串首尾相接得到字符串S,问S[n]

s[1]长度不超过1e6

思路:我们令S[n]所在的字符串为s[ans],那么s中s[ans]以外的字符串都是没有意义的,所以我们只要求s[1]删除ans-1个字母后的字符串中n对应位置N,因为s[i]每次长度-1,所以我们根据原来的n,可以循环求出ans和N。

然后考虑怎么删这ans个字母使字符串最小,先考虑删1个的时候,首先删靠前的字母肯定比删后边的字母好,也就是找最左边的能使字符串变小的位置,所以我们从前往后遍历,如果当前字母大于下一个字母,那么删掉这个字母就会使s[i+1]最小,然后同样再从前往后遍历s[i+1],找到第一个当前字母大于下一个字母的位置并删掉,可以得到s[i+2],如果不存在这样的位置,说明字符串此时是递增的,那么就从后往前逐个删即可。

但这样遍历ans次时间复杂度显然不能接受,通过进一步观察发现,我们要删ans个字母时,在找到一个当前字母大于下一个字母的位置时,要往前检查前面的字母是否也大于下一个字母,如果大于,前边的也要删,所以我们用栈去维护最终的s[ans],只要当前栈顶字母>当前字母,就将栈顶字母弹出,最终栈内的字母形成的字符串的前缀就是s[ans],这个字符串中的第N个字母就是答案,后缀是没有影响的,所以有时不用删完ans次。

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
const int N = 1e6+ 5;
ll n;
void init()
{
}
void solve()
{
	string s;
	cin >> s;
	cin >> n;
	init();
	int cnt = 0;//要删几个字母
	ll now = s.length();
	while (n > now)
	{//循环找到要删几个字母
		n -= now;
		now--;
		cnt++;
	}
	string s2;
	int l = 1;
	vector<char>st;//用栈储存目标字符串
	for (int i = 0; i < s.length(); i++)
	{
		while (!st.empty()&&st.back() > s[i]&&cnt)
		{//栈顶比当前的大就弹出
			st.pop_back();
			cnt--;
		}
		st.push_back(s[i]);
	}
	cout << st[n-1];//vector完爆stack的地方
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值