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;
}

浙公网安备 33010602011771号