题目大意:给定一个字符串,问在执行k次操作后字典序最小的字符串是什么?
一次操作的定义:对于任意一个字符串中的字母s,将字符串内所有为s的字母转换为s-1
即b可以转换为a,c可以转换为b,……,z可以转换为y。
基本思路:
- 因为是要将字符串转换为字典序最小的字符串,因此执行的操作优先考虑下标较小的字符
- 对于操作的理解:对于某个字母m,如果要将其转换为a,则执行m-a次操作,我们可以将整个字符串中的所有[a,m]区间内的字母都转换为a。(可以手动从m开始模拟,理解为逐步翻牌的感觉)
- 转换为代码思路:
- 存储一个mx值来记录当前可以直接转换为a的右边界,即遍历到当前位置的时候,整个字符串中大小在[a,mx]区间内的字母都可以通过最多mx次操作转换为a
- 如果当前字母转换为a的需要的操作数<=k,则直接更新mx值
- 如果当前字母转换为a需要的操作数>k,则该字母最多只能进行(k-mx)次操作;如果当前字母为s,则字符串中[s-(k-mx),s]区间内的字母都可以转换为s-(k-mx),转换为尽可能中最小的字母。
上代码~
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
cin>>T;
for(int t=0; t<T; t++)
{
int n,k;
cin>>n>>k;
string s;
cin>>s;
int mx=0;
/*将字符串通过操作变成字典序最小的字符串,
因此贪心的优先级为对坐标小的字母执行尽可能多又不浪费的操作*/
//在操作过程中,mx记录字符串中通过操作可以变成a的最大右边界;即[0,mx]+'a'的字符都可以变成'a'。
for(int i=0; i<s.size(); i++)
{
int opnumToa=s[i]-'a';
if(opnumToa<=k)
//如果s[i]转换为'a'的次数<k, 表示['a',s[i]]间的所有字母都可以通过s[i]-'a'次转换为'a';
mx=max(mx,opnumToa);
else
//如果该字母变成a的操作次数超过k,表示无法在k次操作内转换为a
{
char minLetter=s[i]-(k-mx);
char maxLetter=s[i];
//之前的字母变成a已经花费了mx次操作,因此这个字母最多只能花费k-mx次操作
//因此即使用(k-mx)操作使[minLetter,maxLetter]区间内的字母都变成minLetter
for(int j=0; j<s.size(); j++)
{
if(minLetter<=s[j]&&s[j]<=maxLetter)s[j]=minLetter;
}
break;
}
}
//最后将[a,mx]区间内的所有字母转换为a
for(int i=0; i<s.size(); i++)
{
int indx=s[i]-'a';
if(indx<=mx)s[i]='a';
}
cout<<s<<endl;
}
return 0;
}