E-Minimax [ Codeforces Round #733 (Div. 1 + Div. 2) ] 构造

链接:https://codeforces.com/contest/1530/problem/E

题意:
给出一个字符串,要求对字符重新排序,使得到的新串以从1-1、1-2、1-3、…、1-n的子串中,前后缀相同的长度最大值最小
输出这个串,且需要字典序最小

.

题解:
可以分多种情况考虑

1.每个字母只有一种:从小到大输出

2.只有一种字母:直接输出

3.第一位放两个最小字母:
此种情况的条件是,最小字母的数量少于一半,使得最小字母可以在后面间位排列,也就是说除了首位之外不能出现最小字母

4.第一位放一个最小字母:
当不满足3时,即只能执行4。先放一个最小字母,再放一个次小字母,然后排满最小字母,这时不能在后面继续追加次小字母了,因为这样会使得最长前后缀增加,所以只能再补一个次次小字母,后面按照从小到大的顺序放就可以
注意特殊情况的处理:当只有两种字母时,只能类似于abbbbbbbaaaaaaaa这样排列

#include<bits/stdc++.h>
using namespace std;
int cnt[27];
void solve(){
    memset(cnt,0,sizeof cnt);
    char fi;
    int kind=0;
    bool flag=0;
    int pos;
    char fir;
    string tmp;
    cin>>tmp;

    for(auto i:tmp){//统计字符数与种类
        if(cnt[i-'a']==0)kind++;
        cnt[i-'a']++;
    }

    if(kind==1){//只有一种直接输出
        cout<<tmp<<endl;
        return;
    }

    for(int i=0;i<26;i++){//定位最小字符的位置
        if(cnt[i]){
            fi='a'+i;
            pos=i+1;
            break;
        }
    }

    for(int i=0;i<26;i++){//查找是否有单个字符
        if(cnt[i]==1){
            flag=1;
            fir='a'+i;
            break;
        }
    }

    if(flag){//如果有单次出现的字符
        cout<<fir;
        cnt[fir-'a']--;
        for(int i=0;i<26;i++){
            if(i!=fir-'a'){
                while(cnt[i]){
                    cout<<char(i+'a');
                    cnt[i]--;
                }
            }
        }
    }else{
        if(cnt[fi-'a']-1<=(tmp.length())/2){//如果可以在开头放两个最小字符

            cout<<fi<<fi;
            cnt[fi-'a']-=2;

            for(int i=2;i<tmp.length();i++){
                if(i%2==0){
                    while(cnt[pos]==0)pos++;
                    cout<<char(pos+'a');
                    cnt[pos]--;
                }else{
                    if(cnt[fi-'a']>0){
                        cout<<fi;
                        cnt[fi-'a']--;
                    }else{
                        while(cnt[pos]==0)pos++;
                        cout<<char(pos+'a');
                        cnt[pos]--;
                    }
                }
            }

        }else{//如果只能在开头放一个最小字符
            if(kind==2){//只有两种字符时特判

                cout<<fi;
                cnt[fi-'a']--;

                while(cnt[pos]==0)pos++;
                while(cnt[pos]>0){
                    cout<<char(pos+'a');
                    cnt[pos]--;
                }

                while(cnt[fi-'a']){
                    cout<<fi;
                    cnt[fi-'a']--;
                }

            }else{//有多于两种字符

                cout<<fi;
                cnt[fi-'a']--;

                while(cnt[pos]==0)pos++;
                cout<<char(pos+'a');
                cnt[pos]--;

                while(cnt[fi-'a']){
                    cout<<fi;
                    cnt[fi-'a']--;
                }

                int tt=pos;
                pos++;
                while(cnt[pos]==0)pos++;
                cout<<char(pos+'a');
                cnt[pos]--;

                pos=tt;
                while(pos<26){
                    while(cnt[pos]){
                        cout<<char(pos+'a');
                        cnt[pos]--;
                    }
                    while(cnt[pos]==0)pos++;
                }

            }
        }
    }
    cout<<endl;
    
}
int main(){
    int t;
    cin>>t;
    while(t--){
        solve();
    }
    //system("pause");
    return 0;
}

一开始只有模糊的想法,没有做出来
看了大佬的博客后,才知道怎么明确的分类
https://www.cnblogs.com/acceptedzhs/p/15028594.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值