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
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;
	vectorst;//用栈储存目标字符串
	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;
}

你可能感兴趣的:(字符串,算法,c++,数据结构)