Codeforces Round #705 (Div. 2) C. K-beautiful Strings(贪心+思维)

C. K-beautiful Strings(贪心+思维)
题意:给你一个字符串长度为N,再给你一个K。要求字符串中每个字符出现的总数是K的倍数,如果不是K的倍数,那么就修改字符串,修改后的字符串比原字符串大,而且输出大于原字符串的最小字符串。
例:
15 3
a b c c c c b b a b c c c c a
a b c c c c b b a c a a a a c
题解:如上述样例第十个字符变成c,后面的变成a然后补充c,这个过程是怎么来的?首先我们应该先处理每个字符的总数,因为修改字符串必然是修改从最后面字符往前修改才会得到最小的,如何修改呢,必然是找到某一个位置然后将当前位置的字符变成下一位字母,然后后面的再用a来补,最后补上原来不用删的的字符。
从后面往前面开始处理,处理的关键是:sum+=(k-vis[i]%k)%k,这个sum代表所有需要变化的字符总数,判断当前是否处理的依据是:sum<=n-i,代表当前处理的字符数小于等于当前i位的字符到n-1位的字符长度,这样这个字符才有足够的长度去变化,当前位置的(赋值一个新的)num=sum变成减去当前位置差的字符个数,然后当前字符vis[ss[i]-‘a’]]- -,再次加上需要 (k-(vis[ss[i]-‘a’])%k)%k增加的个数,最后判断新sum+i <=n那么就可以去变化更新字符,当前位置的字符变成后一位字符,后面的就变成然后之后有剩余的就遍历一下 最后输出需要的就可以了,注意特判N%K!=0的时候,必然是不可以成立的条件,还有特判1。

附上一篇大佬写的

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define fio ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define mse(a,b) memset(a,b,sizeof a)
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=1e9+7;
const int maxx=1e6+10;
const double eps=1e-6;
using namespace std;
const long double PI = 3.14159265358979323846;
//inline ll read(){ ll x=0,f=1; char ch=getchar(); while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); } while (isdigit(ch)) { x=x*10+ch-48;  ch=getchar();  } return x*f;}
//ll cc = ((1ll << 62) - 1 + (1ll << 62));
/*struct node {
    ll l,r ,lazy,val;
    node() :l(),r(),lazy(),val(){}
    node(ll a, ll b, ll c,ll d) :l(a),r(b),lazy(c),val(d){}

};*/

int vis[30];
int n,k;
int get(int x){
    return (k-(x%k))%k;   //计算当前字符是k的倍数
}
signed main()
{
   int t;cin>>t;
   while(t--){
    cin>>n>>k; string ss; cin>>ss;
    if(n%k) cout<<-1<<'\n';   ///特判
    else{ bool flag=false; mse(vis,0); int sum=0;  //初始化
        for(int i=0;i<n;i++) vis[ss[i]-'a']++;    //预处理每个字符的个数
        for(int i=0;i<26;i++) sum+=get(vis[i]);   //初始计算需要改变多少个字符
        if(sum==0) cout<<ss<<endl;
        else{
        for(int i=n-1;i>=0;i--){   //从后往前边遍历每个字符
          sum-=get(vis[ss[i]-'a']); //当前位置的字符 先剪掉所有的
          vis[ss[i]-'a']--;    ///去除掉当前位置的字符
          sum+=get(vis[ss[i]-'a']); //重新计算去除当前的字符后,还需要多少个当前字符才是K的倍数(有可能去除之后的就是K的倍数了)
          if(sum>n-i) continue;///后面修改的位置够了
          for(int j=ss[i]-'a'+1;j<26;j++){ //枚举
            int num=sum; ///赋值
            num-=get(vis[j]); 
            vis[j]++;
            num+=get(vis[j]);  //枚举计算
            if(num+i<=n){    //符合修改
                for(int kk=0;kk<i;kk++)cout<<ss[kk];  //直接输出区间[0-i-1]前面不变的字符
                cout<<char(j+'a');  //输出第i位的改变的字符
                for(int kk=i+1;kk<n-num;kk++) cout<<'a';
                for(int kk=0;kk<26;kk++)
                {
                    int pp=get(vis[kk]);
                    while(pp--) cout<<char(kk+'a');
                }
                cout<<endl;
                flag=true;
            }
          if(flag) break;
            vis[j]--;
          }
                if(flag) break;
        }
        }
    }
   }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值