【蓝桥杯】历届试题 字串排序
这个题目题目只过了70%,剩下30%有点思路,但还没想出具体怎么写,但自己在网上没看到比较好的答案,就把自己的写一下,希望大家可以给点剩下30%的思路。
题目描述:
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要 1次交换。对于字符串 qiao 排序,总共需要4次交换。
小蓝的幸运数字是 ,他想找到一个只包含小写英文字母的字符串,对这个串中的字符进行冒泡排序,正好需要V 次交换。请帮助小蓝找一个这样的字符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
输入格式
输入一行包含一个整数 ,为小蓝的幸运数字。
输出格式
输出一个字符串,为所求的答案。
样例输入
4
样例输出
bbaa
样例输入
100
样例输出
jihgfeeddccbbaa
评测用例规模与约定
试题分析
需要交换的次数,实则就是字符串的逆序数,题目需要求最短的字串,可先求逆序数可以大于等于V的最短的字串的长度。对于长r的字串,其最大逆序数可达r*(r-1)/2,(这里先不考虑英文字母只有26个)。找到相应的长度时,计算最大逆序数-V 的差(这里设为t),然后将最大逆序的字串改变,使其逆序数减少t即可。
具体如何改变才可达到题目要求呢?先从后向前然字母重复两次,同上面两个输出样例一样,这样每两个相同字母,逆序数减一。如果全变成了两个还没达到t呢,即t>r/2时,那接下来重复三次即可。每有三个相同的字母,逆序数将减少3。t最大是r-2,r/3*3 最小是r-2,故全变成3个相同一定可以达到t。下面先上第一次上交简化后的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 1e5+5;
char str[N];
int main(){//字母只有26个,数大了就会超,剩下的还要分情况
// ios::sync_with_stdio(false);
// cin.tie(false);
int n;
cin>>n;
int r=0;
while(r*(r+1)/2<n)r++;
r++;
// cout<<r<<endl;
int t=(r-1)*r/2-n;
// cout<<t<<endl;
str[r-1]='a';
for(int i=r-2;i>=0;i--){
if(t>r/2){
str[i]=str[i+1];
i--;
str[i]=str[i+1];
i--;
t-=3;
}else if(t>0){
str[i]=str[i+1];
i--;t--;
}
if(i>=0)str[i]=str[i+1]+1;
}
puts(str);
return 0;
}
只过了70%,然后分析了一下,字母只有26个,这么算的话V一大就会输出ASCII码比z大的字符了,然后思考了一下V比较大的情况,字串的长r是计算出来了,用下面这段代码可以计算r>26的情况下的r,但具体字串还没想到怎么算,想问问有没有大佬有好的思路。
r=26;
int Max=(r-1)*r/2;
while(Max<n){
r++;
int k=r/26;
int l=r%26;
Max=(r-1)*r/2;
Max-=k*(k-1)/2*26;
Max-=k*l;
}