【牛客网】今日头条2017客户端工程师实习生笔试题

本文解析了四道编程题:回文解码、出专辑、魔法权值及或与加,涉及字符串操作、数学计算、全排列算法及二进制运算。

一、[编程题]回文解码

现在有一个字符串,你要对这个字符串进行 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=5k=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值