今日头条2017后端工程师笔试题
1、最大映射
有 n 个字符串,每个字符串都是由 A-J 的大写字符构成。现在你将每个字符映射为一个 0-9 的数字,不同字符映射为不同的数字。这样每个字符串就可以看做一个整数,唯一的要求是这些整数必须是正整数且它们的字符串不能有前导零。现在问你怎样映射字符才能使得这些字符串表示的整数之和最大?
输入描述:
每组测试用例仅包含一组数据,每组数据第一行为一个正整数 n , 接下来有 n 行,每行一个长度不超过 12 且仅包含大写字母 A-J 的字符串。 n 不大于 50,且至少存在一个字符不是任何字符串的首字母。
输出描述:
输出一个数,表示最大和是多少。
输入例子:
2
ABC
BCA
输出例子:
1875
思路:
给大写字母A~j中的每一个字母赋一个权重,根据权重大小进行排序,然后依次把9~0这十个数字赋值给排序好的字母。可以从低位到高位依次给每个字母赋值为1,10,100,1000,......等。比如以输入为例,赋值及求和过程如下图:
根据题目描述,由于字符映射为的整数不存在前导零,所以需要做相应的处理,使得每个字符串首位置出现的字符都不能为零。如果出现这种情况,需要把不在首位出现的具有最小权值字符提到排好序的数组的首部。
代码如下:
#include<iostream> #include<vector> #include<string> #include<algorithm> #include<numeric> //数值算法 using namespace std; //节点类:每个字符对应一个权重 struct Node { char ch; long long num; Node() {} //默认构造函数 Node(char c, int n) :ch(c), num(n) { } }; //按照权重对节点进行排序 bool cmp(const Node &a, const Node &b) { return a.num < b.num; } //取得每个字符的权重 void getWeight(vector<string> &data, vector<Node> &arr) { for (int i = 0; i < data.size(); ++i) { long long index = 1; for (int j = data[i].size() - 1; j >= 0; --j) { int pos = data[i][j]; arr[pos - 'A'].num += index; index *= 10; } } } int main() { int n; while (cin >> n) { string str; //字符串集合 vector<string> data(n); for (int i = 0; i < n; ++i) { cin >> str; data[i] = str; } //节点类集合 vector<Node> arr(10); for (int i = 'A'; i <= 'J'; ++i) { arr[i - 'A'] = Node(i, 0); } //计算每个字符的权重 getWeight(data, arr); //按照权重由小到大进行排序 sort(arr.begin(), arr.end(), cmp); //每个字符是否在字符串首位置出现过(1:出现过) vector<int> flag(10, 0); for (int i = 0; i < n; ++i) { flag[data[i][0] - 'A'] = 1; } //在排序数组中,第一个没有在字符串首位置出现的字符的位置 int spec_pos = 0; for (; spec_pos < 10; ++spec_pos) { if (flag[arr[spec_pos].ch - 'A'] == 0) break; } Node tmp = arr[spec_pos]; //向后移动一位,把spec_pos位置的元素放在首部 for (int i = spec_pos; i > 0; --i) { arr[i] = arr[i - 1]; } arr[0] = tmp; //将0~9赋值给相应的字符 vector<int> numArr(10); for (int i = 0; i < 10; ++i) { numArr[arr[i].ch - 'A'] = i; } //字符串集合转换为整型数集合 vector<long long> num(n); for (int i = 0; i < n; ++i) { long long val = 0; long long index = 1; for (int j = data[i].size() - 1; j >= 0; --j) { val += numArr[data[i][j] - 'A'] * index; index *= 10; } num[i] = val; } long long count = 0; //对整型数集合求值 cout << accumulate(num.begin(), num.end(), count) << endl; } return 0; }
解析:知识点
--->template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init);
Returns the result of accumulating all the values in the range [first,last)
to init.
--->结构体(回顾下)
附相关参考:
可参考的java代码实现:
import java.util.Arrays; import java.util.Comparator; import java.util.Scanner; class Element{ long w; int flag; } public class Main{ public static void main(String []args){ Scanner cin=new Scanner(System.in); int n; while(cin.hasNext()){ n=cin.nextInt(); String[]str=new String[n]; Element[]e=new Element[10]; for(int i=0;i<10;i++)e[i]=new Element(); for(int i=0;i<n;i++){ str[i]=cin.next(); int l=str[i].length(); long base=1; for(int j=l-1;j>=0;j--,base*=10){ int idx=str[i].charAt(j)-'A'; if(j==0)e[idx].flag=1; e[idx].w+=base; } } Arrays.sort(e, new Comparator<Element>(){ @Override public int compare(Element o1, Element o2) { //if(o1.flag!=o2.flag) return o1.flag-o2.flag; return o1.w>o2.w?1:(o1.w==o2.w?0:-1); } }); long s=0; if(e[0].flag==1){ int k=0; for(;k<10;k++)if(e[k].flag==0)break; Element tmp=e[k]; for(;k>0;k--)e[k]=e[k-1]; e[0]=tmp; } for(int i=9;i>=0;i--){ s+=e[i].w*i; //System.out.println(e[i].flag+" "+e[i].w); } System.out.println(s); } } }
2、木棒拼图
有一个由很多木棒构成的集合,每个木棒有对应的长度,请问能否用集合中的这些木棒以某个顺序首尾相连构成一个面积大于 0 的简单多边形且所有木棒都要用上,简单多边形即不会自交的多边形。
初始集合是空的,有两种操作,要么给集合添加一个长度为 L 的木棒,要么删去集合中已经有的某个木棒。每次操作结束后你都需要告知是否能用集合中的这些木棒构成一个简单多边形。
输入描述:
每组测试用例仅包含一组数据,每组数据第一行为一个正整数 n 表示操作的数量(1 ≤ n ≤ 50000) , 接下来有n行,每行第一个整数为操作类型 i (i ∈ {1,2}),第二个整数为一个长度 L(1 ≤ L ≤ 1,000,000,000)。如果 i=1 代表在集合内插入一个长度为 L 的木棒,如果 i=2 代表删去在集合内的一根长度为 L 的木棒。输入数据保证删除时集合中必定存在长度为 L 的木棒,且任意操作后集合都是非空的。
输出描述:
对于每一次操作结束有一次输出,如果集合内的木棒可以构成简单多边形,输出 "Yes" ,否则输出 "No"。
输入例子:
5
1 1
1 1
1 1
2 1
1 2
输出例子:
No
No
Yes
No
No
思路:
代码如下:
#include <iostream> #include <algorithm> #include <set> using namespace std; //思路:判断几条边能否组成一个简单多边形的基本条件为最长边的值max_len小于其他边的和,亦即是max_len*2<sum_len(所有边的和) multiset<long long> tmp;//multiset自动将元素进行排序,且允许元素重复 int main(){ int n; while(cin>>n){ tmp.clear(); long long sum_len=0; for(int i=0;i<n;i++){ int oper,len; cin>>oper>>len; if(oper==1){ tmp.insert(len); sum_len += len; }else{ tmp.erase(tmp.find(len)); sum_len -= len; } if(tmp.size()<3) cout<<"No"<<endl; else{ long long max_len=*tmp.rbegin(); //排好序的正向最后一个元素就是最大的 if(max_len*2 >= sum_len) cout<<"No"<<endl; else cout<<"Yes"<<endl; } } } return 0; }
可参考的java代码实现:
import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class Main { public static void main(String[] args){ Scanner sc = new Scanner(System.in); List<Integer> list = new ArrayList<Integer>(); List<String> strlist = new ArrayList<String>(); int n = sc.nextInt(); int m = n; int count = 0,sum = 0,max = 0; while(m-- > 0){ int i = sc.nextInt(); int L = sc.nextInt(); if(i == 1){ if(L > max) max = L; count += 1; sum += L; list.add(L); } else if(i == 2){ list.remove((Integer)L); if(L == max){ //需要重新计算max max = 0; for(int j=0;j<list.size();j++){ if(max < list.get(j)) max = list.get(j); } } count -= 1; sum -= L; } if(count <= 2){ strlist.add("No"); } else if(sum - max > max) strlist.add("Yes"); else strlist.add("No"); } for(String s:strlist){ System.out.println(s); } } }
3、魔法权值
给出 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
代码如下:
#include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; int main(){ int n, K; while (cin >> n >> K){ vector<string> str(n); vector<int> vec(n); for (int i = 0; i<n; ++i){ cin >> str[i]; } for (int i = 0; i<n; ++i){ vec[i] = i; } int res = 0; do{ string tmp; for (int i : vec) tmp += str[i]; int num = 1; int len = tmp.size(); for (int i = 1; i<len; ++i){ //左移offset位数后,与原串相等的情况下:每offset个数组成的数据块都相等,如:RAABRAAB,ABABABAB if (i<len / 2){ if (len%i == 0){ string tmp_left(tmp, 0, i); string tmp_right(tmp, i); string new_str = tmp_right + tmp_left; if (new_str == tmp){ if (i == 1){ num += len - 1; break; } ++num; } } } else{ if (len % (len - i) == 0){ string tmp_left(tmp, 0, i); string tmp_right(tmp, i); string new_str = tmp_right + tmp_left; if (new_str == tmp) ++num; } } } if (num == K) ++res; } while (next_permutation(vec.begin(), vec.end())); //next_permutation函数用来计算一组数据的全排列 cout << res << endl; } return 0; }
解析:知识点
--->C++STL的next_permutation 全排列函数
可参考文章:http://www.cnblogs.com/kuangbin/archive/2012/03/30/2424482.html (了解下实现原理)
可参考的java代码实现:
import java.util.Scanner; public class Main{ private static int num; public static void main(String[] args) { Scanner in=new Scanner(System.in); int n=in.nextInt(); int k=in.nextInt(); String[] str=new String[n]; for(int i=0;i<n;i++){ str[i]=in.next(); } in.close(); perm(str,0,str.length-1,k); //输出权值为K的字符串个数 System.out.println(num); } public static String[] swap(String[] buf,int a,int b){ String temp=buf[a]; buf[a]=buf[b]; buf[b]=temp; return buf; } //求全排列 public static void perm(String[] buf,int start,int end,int k){ if(start==end){ String s=""; for(int i=0;i<=end;i++){ s+=buf[i]; } judge(s,k); } else{ for(int i=start;i<=end;i++){ buf=swap(buf,start,i); perm(buf,start+1,end,k); buf=swap(buf,start,i); } } } //判断权值 public static void judge(String S,int k){ int count=0; String string=S+S; for(int i=1;i<S.length()+1;i++){ if(S.equals(string.substring(i,i+S.length()))){ count+=1; } } if(count==k){ num++; } } }
4、或与加
给定 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
代码如下:
#include <iostream> using namespace std; int main() { long long x, k; while(cin>>x>>k){ long long res=0; long long bitN=1; while(k){ if((x & bitN) ==0){ res += (bitN*(k&1)); k>>=1; } bitN<<=1; } cout<<res<<endl; } return 0; }
思路:
import java.util.Scanner; public class Main { public static void main(String[] args) { long x; long k; long y = 0; Scanner scanner = new Scanner(System.in); x = scanner.nextLong(); k = scanner.nextLong(); long flag = 1; while(k != 0) { if((x & flag) == 0) { y += (flag * (k & 1)); k >>= 1; } flag <<= 1; } System.out.println(y); } }