今日頭條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.

--->結構體(回顧下)

附相關參考:

C語言結構體(struct)常見使用方法

構造函數對結構體初始化的影響。

結構體常見錯誤

 

可參考的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;
}

思路:

此題容易用下列代碼描述
bool is_eq(x, y) {
    return x + y == x | y;
}
然后整個循環從 1 到 y,y 是 第 k 個 滿足 is_eq() 的數。這樣做沒錯,但是 測試用例給整個:
x = 1073741802, k = 1073741823 這么大的數,顯然暴力窮舉是不合適的。
 
不過可以舉幾個數字組合來找其中的規律:
例如:
k = 1 時,5 + 2 == 5 | 2 
k = 2 時,5 + 8 == 5 | 8 
k = 3 時,5 + 10  == 5 | 10 
k = 4 時,5 + 16  == 5 | 16 
k = 5 時,5 + 18  == 5 | 18 
 
轉二進制
   
滿足這個運算規律 x + y == x | y 的二進制有:
0 + 0 == 0 | 0
1 + 0 == 1 | 0
1 + 1 !=  1 | 0 (只有這個不滿足)
所以 x y 各自相對應的二進制位不能同時為 1,換言之, x 中 當前位 為 1 時, 與之對應的 y 那一位 肯定是 0
所以 x 位為 1 的就確定了,可以去除1
 
X
 
Y
 
 
將 Y 中紅色 的 0 去掉看看,得到一組新數據
 
 
這正是 從 1 2 3 4 5 6 7,由於 y 表是按照 k 從1遞增的順序得到的值。

 

算法大概是,將 x 和 y 都轉成 二進制串, 然后將 y 的二進制串依次塞進 x 串中為 0 的部位,得到的一個新值,
把這個值中原先 x 為 1 的 位 都給改成 0,就能得到 y 值。
 
比如 k = 3 = b( 1  1), x = 5 = b(0101)
第一步將 k 塞入 x, 得到 b( 11 11), 第二步將原先 x 中為 1 的變成 0, 得到 b( 10 10) , 即 y = 10
 
可參考的java代碼實現:
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);
    }
}

  

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM