F.小沙的串串

F.小沙的串串

题目描述

小沙有一个长度为 n 的字符串,他有 k 次操作,每次操作可以任意选择一个字符,将其移动到字符串的最后。 小沙想问你,找到恰好操作 k 次之后,字典序最大的字符串是怎样的。

输入描述:

第一行输入两个整数 n ,k ,分别代表字符串的长度以及操作的次数 \(1 \le n \le 10^5\)\(1 \le k \le 10^9\)

第二行输入一行长度为 n 字符串 ,该字符串仅包含小写字母。

输出描述:

输出一行字符串代表答案。              

输入

5 3
ebcdd

输出

eddcb

思路点拨1:

  • 利用单调栈,维护一个单调递减区间。

  • 这样考虑,当且仅当k不足以使整个序列都调整一遍时。

    • 考虑从头开始移动,且移动原则正好满足单调栈的特点
    • 当然题目为字符串,为直观比较大小,我用数字代表字母进行举例。
    • e.g. 123456,当k = 2时,只能移动前两位,让3作为第一位最大,然后1,2降序放到后面。
    • e.g. 143222, 当k = 2时,同样从头开始移动,首先移动到第二位时4比较大,并且都大于后面的,并且后面是不严格单调下降的序列,所以没有选择的一位,可以从43222后面开始选择,选择2放到1里面,然后1,2降序放到后面
  • 当k足以使整个序列都调整一遍时,即 k > n时

    • 只需要操作n次即可,或者说操作使原序列变成不严格单调下降的序列的次数即可。

思路点拨2:

jiangly大佬赛时代码思路(自己部分理解)

  • 反向记忆化,思维

  • 当k >= n 时,直接对原序列倒序输出即可

  • 反之,nxt二维数组记忆化,记忆化原因,前面选过的元素不会对后面的选择影响。

    • 先筛选出字典序最大的前缀,然后在倒序添加后缀,

    • 筛选原则:nxt[j][x] <= k + i,x从大到小筛选;

    • 注意下标都是从零开始

    • 我们要筛选的是前缀,所以必须范围从k (这里是下标从0开始,其实是考虑的前 k + 1个字符)开始,因为前k个我们都是可以放到后面,然后把 第 k + 1个放到最前面,所以每次的范围长度其实是k + 1,e.g.(k = 2, 范围一开始为 [0,2],三个数),当且仅当选择一个最大的,然后下一次的第一维(第几位的由后往前的记忆化结果)是当前的第一维+1,因为要去除该位的影响。

    • 一定要注意筛选的是前缀,所以应该筛选出n - k个,每一次循环条件是i++,因为范围是k + i,所以每一次范围必须要+1。

    • 听不懂了吧,我也不知道自己说的什么,hhh,那就举一个案例说明一下吧,还是利用数字直观举例

    • e.g. string = 12145,当k = 2 时,

      • 第一次循环,j下标为0,

        $nxt[0][0] = 0,nxt[0][1] = 1, nxt[0][2] = n = 5,nxt[0][3]=3,nxt[0][4] = 4 $,从121中选择最大的一个2;

      • 然后下一次移动到j = 2的位置,同理j = 2时,$nxt[0][0] = 2,nxt[0][1] = = n = 5, nxt[0][2] = n = 5,nxt[0][3]=3,nxt[0][4] = 4 $;

      • 然后从14中选择4,然后下一次移动到 j = 4的位置,这时候\(nxt[0][0]= nxt[0][1] =nxt[0][2] =nxt[0][3]= n = 5,nxt[0][4] = 4\)

      • 然后从5中选择5,所以前缀为245,后缀为11,最终结果为24511。

提交代码

单调栈思路:

// Problem: 小沙の串串
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/46813/F
// Memory Limit: 524288 KB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n, k;
	string s,ans, res;
	cin >> n >> k >> s;
	
	for(char c : s)
	{
		while(k && ans.size() && ans.back() < c)
		{
			res.push_back(ans.back());
			ans.pop_back();
			k--;
		}
		ans.push_back(c);
	}
	while(k && ans.size())
	{
		k--;
		res.push_back(ans.back());
		ans.pop_back();
	}
	
	sort(res.begin(), res.end());
	reverse(res.begin(), res.end());
	
	ans += res;
	cout << ans << "\n";
	return 0;
}

jiangly赛时代码

// Problem: 小沙の串串
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/46813/F
// Memory Limit: 524288 KB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	
	int n, k;
	std::cin >> n >> k;
	
	k = std::min(n, k);
	
	std::string s;
	std::cin >> s;
	
	std::vector nxt (n + 1, std::array<int, 26>{});
	
	nxt[n].fill(n);
	for(int i = n - 1; ~i; i--)
	{
		nxt[i] = nxt[i + 1];
		nxt[i][s[i] - 'a'] = i;
	}
	
	std::string ans;
	std::array<int,26> cnt{};
	
	for(auto x:s)
	{
		cnt[x - 'a']++;
	}
	
	for(int i = 0, j = 0; i < n - k; i++)
	{
		for(int x = 25; ; x--)
		{
			if(nxt[j][x] + n -k -i <= n)
			{
				ans += 'a' + x;
				cnt[x]--;
				j = nxt[j][x] + 1;
				break;
			}
		}
	}
	for(int x = 25; ~x; x--)
	{
		while(cnt[x]--)
		{
			ans += 'a' + x;
			
		}
	}
	
	std::cout << ans << "\n";
	return 0;
}
posted @ 2023-02-24 22:44  哲远甄骏  阅读(44)  评论(0)    收藏  举报