一、[编程题]回文解码
现在有一个字符串,你要对这个字符串进行 n次操作,每次操作给出两个数字:(p, l)表示当前字符串中从下标为 p的字符开始的长度为 l的一个子串。你要将这个子串左右翻转后插在这个子串原来位置的正后方,求最后得到的字符串是什么。字符串的下标是从 0开始的,你可以从样例中得到更多信息。
输入描述:
每组测试用例仅包含一组数据,每组数据第一行为原字符串,长度不超过 10,仅包含大小写字符与数字。接下来会有一个数字 n表示有 n 个操作,再接下来有 n行,每行两个整数,表示每次操作的(p , l)。
保证输入的操作一定合法,最后得到的字符串长度不超过 1000。
输出描述:
输出一个字符串代表最后得到的字符串。
输入例子:
ab
2
0 2
1 3
输出例子:
abbaabb
c++代码如下:
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
int main(){
int n; //有n次操作
string s;
cin>>s;
cin>>n;
int i,j;
i=1;
//一共n次操作
while(i<=n){
int a,b;
cin>>a>>b; //代表一行的两个数字
string s1; //存储子串
s1= s.substr(a, b);
string s2=s1; //存储子串的逆序串
reverse(s2.begin(),s2.end());
s.insert(a+b,s2);
i++;
}//while
cout<<s<<endl;
return 0;
}二、[编程题]出专辑
你作为一名出道的歌手终于要出自己的第一份专辑了,你计划收录 n首歌而且每首歌的长度都是 s秒,每首歌必须完整地收录于一张 CD当中。每张 CD的容量长度都是 L 秒,而且你至少得保证同一张 CD内相邻两首歌中间至少要隔 1秒。为了辟邪,你决定任意一张 CD内的歌数不能被 13 这个数字整除,那么请问你出这张专辑至少需要多少张 CD?
输入描述:
每组测试用例仅包含一组数据,每组数据第一行为三个正整数 n, s, L。保证 n ≤ 100 , s ≤ L ≤ 10000
输出描述:
输出一个整数代表你至少需要的 CD数量。
输入例子:
7 2 6
58 4 163
27 1 27
输出例子:
4
2
3
思路:
先算出每个CD可以装下的歌曲数量num_eachcd,假设一个CD有t首歌,那么一个CD的容量为t*s+t-1=l,可以推出t = (l+1)/(s+1),所以num_eachcd = (l+1)/(s+1)。
由题意可知每个CD的歌曲数量不能是13的倍数,所以如果num_eachcd%13=0时,num_eachcd要减1。
一共有n首歌,用num_cd表示需要的CD数,那么num_cd=(n/num_eachcd)。如果num_eachcd不能被n整除,说明有几首歌被剩下了,此时num_cd应加1。
如果num_cd=1,说明一张CD就可以放下了。总数是n首歌,说明一张CD放了n首歌,如果n%13=0,那么还需要加一张CD。
如果num_cd>1,说明要多张CD才可以放下。一开始已经判断了num_eachcd不会是13的倍数了,所以还需要判断一下最后可能剩下的n%num_eachcd是否是13的倍数。如果num_lastcd=n%num_eachcd是13的倍数,但是num_eachcd比num_lastcd大不止1,那么可以从num_eachcd中分一或者两首歌到最后这个CD中。因为显然num_eachcd减1或者2后肯定有一个情况不会是13的倍数。但是如果num_lastcd是13的倍数,且num_eachcd比num_lastcd大1,此时不管怎么补都无法实现所有的CD都不是13的倍数,且一个CD的容量不溢出。此时只能再添加一个CD。
c++代码:
#include<iostream>
using namespacestd;
int main(){
int n,s,l; //n首歌,每首歌的长度s秒,每张CD的容量长度是l秒
while(cin>>n>>s>>l){
//先算出一张cd的歌
int num_eachcd = (l+1)/(s+1);
if(num_eachcd % 13 ==0){
//如果每张cd的歌的数量是13的倍数,那么cd的歌曲数量要减一
num_eachcd--;
}
//算是不是会剩下几首歌正好是13的倍数
int num_cd = n/num_eachcd;
//说明有几首歌单出来了
if(n % num_eachcd > 0){
num_cd++;
}
if(num_cd == 1){
//一张CD放得下
if(n%13 == 0){
num_cd++;
}
}else{
//需多张cd
//如果多出来的几首歌正好是13的倍数,并且前面的cd的歌曲都只多一首
if((n%num_eachcd)%13 == 0&& num_eachcd-1 == n%num_eachcd){
num_cd++;
}
}//else
cout<<num_cd<<endl;
} //while
return 0;
}三、[编程题]魔法权值
给出 n个字符串,对于每个 n个排列 p,按排列给出的顺序(p[0] , p[1] …p[n-1])依次连接这 n个字符串都能得到一个长度为这些字符串长度之和的字符串。所以按照这个方法一共可以生成 n!个字符串。
一个字符串的权值等于把这个字符串循环左移 i次后得到的字符串仍和原字符串全等的数量,i的取值为 [1 , 字符串长度]。求这些字符串最后生成的 n!个字符串中权值为 K的有多少个。
注:定义把一个串循环左移 1次等价于把这个串的第一个字符移动到最后一个字符的后面。
输入描述:
每组测试用例仅包含一组数据,每组数据第一行为两个正整数 n, K, n的大小不超过 8 , K不超过 200。接下来有 n行,每行一个长度不超过 20且仅包含大写字母的字符串。
输出描述:
输出一个整数代表权值为 K的字符串数量。
输入例子:
3 2
AB
RAAB
RA
输出例子:
3
思路:本题主要是理清题意,我一开始就没有看懂题目。还是谷歌了之后在别人的帮助下搞清楚题意的。
第一行输入n和k,n表示有n个字符串,k表示权值。接下来有n行输入
AB
RAAB
RA
重点理解一下k要表达的含义:k代表一个字符串的权值,最终结果让程序输出。权值等于k的数量,而k等于一个字符串循环左移i次后得到的字符串仍和原字符串全等的数量,由于i的取值为1到字符串长度len,所以k的最小值是1,因为任何一个字符串左移长度len后都和原字符串相等。
k的含义搞清楚了,下面求字符串。由题目可知会生成n!种字符串,那么能想到全排列的情况,假设用1表示第一个字符串AB,2表示第二个字符串RAAB,3表示第三个字符串RA,那么根据全排列一共有6种情况。
1 2 3:AB RAAB RA(偏移后和原串相等的偏移量i=4、8),则k=count(i)=2
1 3 2:AB RA RAAB(偏移量i=8),k=1
2 1 3:RAAB AB RA(偏移量i=8),k=1
2 3 1:RAAB RA AB(偏移量i=4、8),k=2
3 1 2:RA AB RAAB(偏移量i=4、8),k=2
3 2 1:RA RAAB AB(偏移量i=8),k=1
则输出k==2的数量,count(k==2)=3
所以输出为3
c++代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
/*
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
case通过率为60.00%
*/
using namespace std;
int main(){
int n,k; //n个字符串,k是权值
while(cin>>n>>k){
int i,j;
string tmp;
vector<string> str;
vector<int> str_num; //用str_num来进行字符串的全排列
for(i=0;i<n;i++){
//输入n个字符串
cin>>tmp;
str.push_back(tmp);
str_num.push_back(i);
}//for
int count=0; //用来计数权值为k的字符串个数
//先进行第一个全排列字符串的权值判断
int each_count=0; //用来计数每个字符串进行循环左移动过程中等于原字符串的次数
string str1,str2;
for(i=0;i<n;i++){
str1+=str[i];
}
int len=str1.length(); //这个字符串的长度可以通用
//进行全排列
do{
//先拼接字符串
str1 = "";
str2 = ""; //先清空字符串
each_count=0;
for(i=0;i<n;i++){
str1 +=str[str_num[i]];
}
//cout<<str1<<endl;
for(i=1;i<=len;i++){
str2=str1.substr(i,len-i)+ str1.substr(0,i);
if(str1.compare(str2)==0){
each_count++;
}
}
if(each_count==k){
count++;
}
}while(next_permutation(str_num.begin(),str_num.end()));
cout<<count<<endl;
}//while
return 0;
}上面的代码只对了60%,提示超时了
通过谷歌看了一下别人的思路,在对字符串进行偏移和比较部分还可以进一步优化。优化后的代码AC成功,如下:
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
int main(){
int n,k; //n个字符串,k是权值
while(cin>>n>>k){
int i,j;
string tmp;
vector<string> str;
vector<int> str_num; //用str_num来进行字符串的全排列
for(i=0;i<n;i++){
//输入n个字符串
cin>>tmp;
str.push_back(tmp);
str_num.push_back(i);
}//for
int count=0; //用来计数权值为k的字符串个数
//先进行第一个全排列字符串的权值判断
//进行全排列
do{
//先拼接字符串
string str1;
int each_count=1; //用来计数每个字符串进行循环左移动过程中等于原字符串的次数
for(i=0;i<n;i++){
str1 +=str[str_num[i]];
}
int len = str1.length();
for(i=1;i<len;++i){
//左移offset位数后,与原串相等的情况下:每offset个数组成的数据块都相等,如RAABRAAB,ABABABAB
if(i<len/2){
if(len%i ==0){
stringtmp_left(str1, 0, i);
stringtmp_right(str1, i);
stringnew_str=tmp_right+tmp_left;
if(new_str==str1){
if(i==1){
each_count+=len-1;
break;
}
++each_count;
}
}
}else{
if(len%(len-i)==0){
stringtmp_left(str1, 0, i);
stringtmp_right(str1, i);
stringnew_str=tmp_right+tmp_left;
if(new_str==str1)
++each_count;
}
}
}
if(each_count==k){
count++;
}
}while(next_permutation(str_num.begin(),str_num.end()));
cout<<count<<endl;
}//while
return 0;
}四、[编程题]或与加
给定 x, k,求满足 x + y = x | y的第 k 小的正整数 y 。 |是二进制的或(or)运算,例如 3 | 5 = 7。
比如当 x=5,k=1时返回 2,因为5+1=6 不等于 5|1=5,而 5+2=7等于 5 | 2 = 7。
输入描述:
每组测试用例仅包含一组数据,每组数据为两个正整数 x , k。满足 0 < x , k ≤ 2,000,000,000。
输出描述:
输出一个数y。
输入例子:
5 1
输出例子:
2
首先要理解题目,我最初看题目的时候并不是很理解。初始给定一个x和k,我们要从1开始找,找到满足x+y = x|y 的数。当找到第k个这样的数时停止,并输出此时的y。
一、最笨的方法:
y取1~INT_MAX,只要满足x+y = x|y 就计数,当数到第k个满足条件的数时停止,输出此时的y。这样的方法肯定会超时。
c++代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<limits.h>
/*
您的代码已保存
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
case通过率为20.00%
*/
using namespace std;
int main(){
int x,k; //给定的x和k
while(cin>>x>>k){
int count=0; //记录当前是第几个满足条件的数
for(int i=1;i<INT_MAX;i++){
if((x|i) == (x+i)){
count++;
}
if(count == k){
cout<<i<<endl;
break;
}
}//for
} //while
return 0;
}二、大神的想法
首先看条件x+y=x|y
这里先推一个结论:对于x&y = 0,从二进制上看,x取1的地方,y必定不能取1。从最低位考虑,若x与y在某一位上同时取1,则x+y在该位上为0,x|y在该位上为1,前面说着是最低一位x y同时取1,也就是说没有更低位加法的进位,所以这里两个结果不相等,出现了矛盾。
例子:
x = 001010
y = 110110
x + y = 1000000
x | y = 111110
偏差产生的原因是倒数第二位,x+y = 0 x|y=1 且倒数第一位加法没有进位。
结论:x在二进制取1的位上,y不能作出改变,只能取0。
有了上述结论,可以进一步推出只要在x取0的地方,y可以作出改变。
例如:
x = 10010010011
y =00000000(0)00 k=0
y =00000000(1)00 k=1
y =0000000(1)(0)00 k=2
y =0000000(1)(1)00 k=3
y =00000(1)0(0)(0)00 k=4
y =00000(1)0(0)(1)00 k=5
y =00000(1)0(1)(0)00 k=6
y =00000(1)0(1)(1)00 k=7
注意观察括号里的数,都是x取0的比特位,如果把括号里的数连起来看,正好等于k。得出结论:把k表示成二进制,填入x取0的比特位,x取1的比特位保持为0,得到y。
注意当把x中间所有的0填完后,如果此时k的二进制还没遍历完,要往x的前面继续添加k的二进制。因此结果的result要定义为long long,如果定义为int会溢出。
按照上面的思路,我写的c++代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
int x,k; //x+y=x|y 的第k小的正整数y
while(cin>>x>>k){
vector<int> x_binary; //存x化成二进制数的各位
vector<int> k_binary;
while(x!=0){
x_binary.push_back(x%2);
x=x/2;
}//while
int i,j;
while(k!=0){
k_binary.push_back(k%2);
k=k/2;
}//while
int x_len=x_binary.size();
int k_len=k_binary.size();
j=0;
for(i=0;i<x_len ||j<k_len;i++){
if(i<x_len){
if(x_binary[i] ==1){
x_binary[i]=0;
}else{
if(j<k_len){
x_binary[i]= k_binary[j];
j++;
}else{
x_binary[i]= 0;
}
}
}else{
x_binary.push_back(k_binary[j]);
j++;
}
}
long long result=0;
for(i=x_binary.size()-1;i>=0;i--){
result =result*2+x_binary[i];
}
cout<<result<<endl;
}
return 0;
}
大神的代码:
#include<iostream>
using namespace std;
int main()
{
long long x, k;
cin>>x>>k;
long long bitNum = 1;
long long ans = 0;
while(k)
{
if((x& bitNum) == 0)
{
ans+= (bitNum * (k & 1));
k>>= 1;
}
bitNum<<= 1;
}
cout<<ans<<endl;
return0;
}
本文解析了四道编程题:回文解码、出专辑、魔法权值及或与加,涉及字符串操作、数学计算、全排列算法及二进制运算。

794

被折叠的 条评论
为什么被折叠?



