字节2024春招笔试题汇总

本文章第五题稍有难度,可直接跳至第五题

P1

题目描述

万万没想到之聪明的编辑
我叫王大锤,是一家出版社的编辑。我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误。但是,优秀的人总能在平凡的工作中发现真理。我发现一个发现拼写错误的捷径:

1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello
2. 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello
3. 上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC

我特喵是个天才!我在蓝翔学过挖掘机和程序设计,按照这个原理写了一个自动校对器,工作效率从此起飞。用不了多久,我就会出任CEO,当上董事长,迎娶白富美,走上人生巅峰,想想都有点小激动呢!
……
万万没想到,我被开除了,临走时老板对我说: “做人做事要兢兢业业、勤勤恳恳、本本分分,人要是行,干一行行一行。一行行行行行;要是不行,干一行不行一行,一行不行行行不行。” 我现在整个人红红火火恍恍惚惚的……

请听题:请实现大锤的自动校对程序

数据范围: 1 \le n \le 50 \1≤n≤50  ,每个用例的字符串长度满足 1 \le l \le 1000 \1≤l≤1000 
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
第一行包括一个数字N,表示本次用例包括多少个待校验的字符串。

后面跟随N行,每行为一个待校验的字符串。
输出描述:
N行,每行包括一个被修复后的字符串。
示例1
输入例子:
2
helloo
wooooooow
输出例子:
hello
woow
示例2
输入例子:
1
nowcoder
输出例子:
nowcoder

思路

简单的字符串遍历,记录前面字符出现的次数按要求生成新字符即可。

代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int n = Integer.parseInt(sc.nextLine());
       while(n-- > 0){
        char[] str = sc.nextLine().toCharArray();
        char[] res = func(str);
        System.out.println(new String(res));
       }
    }
    public static char[] func(char[] str){
        String res = new String();
        int pre = 0;
        int cur = 1;
        res += str[0];
        for(int i = 1;i < str.length;i++){
            if(str[i] == str[i - 1]){
                cur++;
                if(cur == 3){
                    cur--;
                    continue;
                }
                if(pre == 2 && cur == 2){
                    cur--;
                    continue;
                }
            }else{
                pre = cur;
                cur = 1;
            }
            res += str[i];

        }
        return res.toCharArray();
    }
}

P2

题目描述

2.
万万没想到之抓捕孔连顺
我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工,我提议

1. 我们在字节跳动大街的 N 个建筑中选定 3 个埋伏地点。
2. 为了相互照应,我们决定相距最远的两名特工间的距离不超过 D 。

我特喵是个天才! 经过精密的计算,我们从X种可行的埋伏方案中选择了一种。这个方案万无一失,颤抖吧,孔连顺!
……
万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在cosplay的队伍中逃出了字节跳动大街。只怪他的伪装太成功了,就是杨过本人来了也发现不了的!

请听题:给定 N(可选作为埋伏点的建筑物数)、 D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:
1. 两个特工不能埋伏在同一地点
2. 三个特工是等价的:即同样的位置组合( A , B , C ) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用


数据范围: 0 < n,d\le 10^6    0<n,d≤10 
6
  
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 128M,其他语言256M
输入描述:
第一行包含空格分隔的两个数字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)

第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000])表示,从小到大排列(将字节跳动大街看做一条数轴)
输出描述:
一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模
示例1
输入例子:
4 3
1 2 3 4
输出例子:
4
例子说明:
可选方案 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)   
示例2
输入例子:
5 19
1 10 20 30 50
输出例子:
1
例子说明:
可选方案 (1, 10, 20)   
示例3
输入例子:
2 100
1 102
输出例子:
0
例子说明:
无可选方案 

思路

因为大楼位置为递增数组,明显可看出单调性。可选择滑动窗口固定一个位置后按排列组合求解。

除此之外对于选位置的题目可以选择dp背包要不要问题,但时间复杂度明显大于On,按数据量推断求解即可。

此处只出示最优解,按滑动窗口求出固定左侧位置的最大范围后按照排列组合求解。

代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String[] nd = sc.nextLine().split(" ");
        int n = Integer.parseInt(nd[0]);
        long d = Integer.parseInt(nd[1]);
        long[] nums = new long[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextLong();
        }
        System.out.println(func(nums, d));
    }
    public static long func(long[] nums, long d) {
        long res = 0;
        for (int i = 0, j = 0; i < nums.length; i++) {
            while ( j < nums.length && nums[j] - nums[i] <= d) {
                j++;
            }
            if (j - i >= 3) {
                res += (((long)(j - i - 1) * (long)(j - i - 2) / 2) + 99997867 ) % 99997867;
            }
            res %= 99997867;
        }
        return res % 99997867;
    }
}

P3

题目描述

3.

雀魂启动!

小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少。

于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七对子等),具体的规则如下:

  1. 总共有36张牌,每张牌是1~9。每个数字4张牌。
  2. 你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌
  • 14张牌中有2张相同数字的牌,称为雀头。
  • 除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777)

例如:

1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌

1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组123,123,567,789的四个顺子,可以和牌

1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。

现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 32M,其他语言64M

输入描述:

输入只有一行,包含13个数字,用空格分隔,每个数字在1~9之间,数据保证同种数字最多出现4次。

输出描述:

输出同样是一行,包含1个或以上的数字。代表他再取到哪些牌可以和牌。若满足条件的有多种牌,请按从小到大的顺序输出。若没有满足条件的牌,请输出一个数字0

示例1

输入例子:

1 1 1 2 2 2 5 5 5 6 6 6 9

输出例子:

9

例子说明:

可以组成1,2,6,7的4个刻子和9的雀头

示例2

输入例子:

1 1 1 1 2 2 3 3 5 6 7 8 9

输出例子:

4 7

例子说明:

用1做雀头,组123,123,567或456,789的四个顺子

示例3

输入例子:

1 1 1 2 2 2 3 3 3 5 7 7 9

输出例子:

0

例子说明:

来任何牌都无法和牌

思路

数字只有1-9所以9个长度的数组即可,之后按照回溯的思路尝试各牌型即可。

通过win函数判断是否可以组成条件牌型,win函数为判断算法,统计刻子、顺子、雀头数量,通过回溯算法确认。

代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] cards = new int[9]; //1 - 9 ~ 0 - 8
        String[] s = sc.nextLine().split(" ");
        for (int i = 0; i < s.length; i++) {
            int card = Integer.parseInt(s[i]) - 1;
            cards[card]++;
        }
        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < 9; i++) {
            cards[i]++;
            if (cards[i] <= 4 && win(cards)) {
                res.add(i + 1);
            }
            cards[i]--;
        }
        for (int i = 0; i < res.size() - 1; i++) {
            System.out.print(res.get(i) + " ");
        }
        if (!res.isEmpty()) {
            System.out.print(res.get(res.size() - 1));
        }
    }
    public static boolean win(int[] arr) {
        int[] cards = arr.clone();
        return  process(cards, 0, 0, 0);
    }
                                      //牌型      当前牌    刻子/顺子     雀头
    public static boolean process(int[] cards, int index, int path, int dou) {
        if (path == 4 && dou == 1) {
            return true;
        }
        if (index >= 9) {
            return false;
        }
        if (cards[index] < 0) {
            return  false;
        }
        if (dou > 1) {
            return  false;
        }
        if (cards[index] > 4) {
            return  false;
        }
        boolean p1 = false;
        boolean p2 = false;
        boolean p3 = false;
        if (cards[index] >= 3) {
            cards[index] -= 3;
            p1 = process(cards, index, path + 1, dou);
            cards[index] += 3;
        }
        if (cards[index] >= 2 && dou != 1) {
            cards[index] -= 2;
            p2 = process(cards, index + 1, path, dou + 1);
            cards[index] += 2;
        }
        if (index < 7 && cards[index] >= 1 && cards[index + 1] >= 1 &&
                cards[index + 2] >= 1) {
            cards[index]--;
            cards[index + 1]--;
            cards[index + 2]--;
            p3 =  process(cards, index, path + 1, dou);
            cards[index]++;
            cards[index + 1]++;
            cards[index + 2]++;
        }
        boolean p4 = false;
        p4 = process(cards, index + 1, path, dou);
        return p1 || p2 || p3 || p4;
    }
}

P4

题目描述

4.
特征提取
       小明是一名算法工程师,同时也是一名铲屎官。某天,他突发奇想,想从猫咪的视频里挖掘一些猫咪的运动信息。为了提取运动信息,他需要从视频的每一帧提取“猫咪特征”。一个猫咪特征是一个两维的vector<x, y>。如果x_1=x_2 and y_1=y_2,那么这俩是同一个特征。
       因此,如果喵咪特征连续一致,可以认为喵咪在运动。也就是说,如果特征<a, b>在持续帧里出现,那么它将构成特征运动。比如,特征<a, b>在第2/3/4/7/8帧出现,那么该特征将形成两个特征运动2-3-4 和7-8。
现在,给定每一帧的特征,特征的数量可能不一样。小明期望能找到最长的特征运动。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
第一行包含一个正整数N,代表测试用例的个数。

每个测试用例的第一行包含一个正整数M,代表视频的帧数。

接下来的M行,每行代表一帧。其中,第一个数字是该帧的特征个数,接下来的数字是在特征的取值;比如样例输入第三行里,2代表该帧有两个猫咪特征,<1,1>和<2,2>
所有用例的输入特征总数和<100000

N满足1≤N≤100000,M满足1≤M≤10000,一帧的特征个数满足 ≤ 10000。
特征取值均为非负整数。
输出描述:
对每一个测试用例,输出特征运动的长度作为一行
示例1
输入例子:
1
8
2 1 1 2 2
2 1 1 1 4
2 1 1 2 2
2 2 2 1 4
0
0
1 1 1
1 1 1
输出例子:
3
例子说明:
特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出3

思路

通过两个TreeSet取出每次更新后最大的连续特征值,为保证连续,只有之前连续出现过的才会存在pre中,通过preSet 和 curSet的交换完成对连续的判断与更新。两个set的来回更新也保证了空间复杂度。通过nodeMap记录特征值,找到对应的Node Node为特征值与连续出现次数。nodeMap的使用是因为node无法通过特征值方便获取,或许有更优解可以去掉nodeMap

代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static class Node {
        public String str;
        public int val;
        public  Node(String s, int v) {
            str = s;
            val = v;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        while (n-- > 0) {
            int m = Integer.parseInt(sc.nextLine());
            TreeSet<Node> cur = new TreeSet<>((o1, o2) -> {
                return o2.val == o1.val ? o2.hashCode() - o1.hashCode() : o2.val - o1.val;
            });
            TreeSet<Node> pre = new TreeSet<>((o1, o2) -> {
                return o2.val == o1.val ? o2.hashCode() - o1.hashCode() : o2.val - o1.val;
            });
            HashMap<String, Node> nodeMap = new HashMap<>();
            int res = 0;
            for (int i = 0; i < m; i++) {
                String[] s = sc.nextLine().split(" ");
                int k = Integer.parseInt(s[0]);
                int index = 1;
                while (k-- > 0) {
                    String str =   s[index++] + "_" + s[index++];
                    Node node;
                    if (nodeMap.containsKey(str)) {
                        node = nodeMap.get(str);
                    } else {
                        node = new Node(str, 1);
                        nodeMap.put(str, node);
                    }
                    if (pre.contains(node)) {
                        node.val++;
                        cur.add(node);
                    } else {
                        node.val = 1;
                        cur.add(node);
                    }
                    res = Math.max(res, cur.first().val);
                }
                pre = (TreeSet<Node>) cur.clone();
                cur.clear();
            }
            nodeMap.clear();
            System.out.println(res);
        }
    }
}

P5

题目描述

5.
毕业旅行问题
小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
城市个数n(1<n≤20,包括北京)

城市间的车票价钱 n行n列的矩阵 m[n][n]
输出描述:
最小车费花销 s
示例1
输入例子:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出例子:
13
例子说明:
共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,依次类推。假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况。

思路

两种思路:

1.可以通过dfs,类似图的遍历,通过visited数字求解,时间复杂度过高。这里不提供代码了

2.dp求解,城市个数小于20所以可以利用位运算优化,类似的算法都可采用位运算优化,回溯、图的遍历、旅行家问题等。

优化思路:通过一个int位可表示32个位信息,每个位上0,1代表true、false,所有使用boolean数组的题目都应考虑此方式优化。这样可简化为两个可变参数,通过备忘录法即可通过。

递归思路:

递归出口:当所有城市都已到达,回到0位置return

每一步的递归思路:选择一个未去过的城市,深度遍历全部走完后,取代价最小的方案

距离:确定从0-1后 要想最小需要从1走完所有城市最小,再尝试从1-2依次递归。

代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static int fin = 1;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        int[][] grap = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                grap[i][j] = sc.nextInt();
            }
        }
        //从0开始往下走,回到0
        int res = Integer.MAX_VALUE;
        for (int i = 1; i < n; i++) {
            fin |= (1 << i);
        }
        int[][] dp = new int[n][fin + 1];
        for (int i = 1; i < n; i++) {
            int visited = (1 << i) | 1;
            res = Math.min(res, grap[0][i] + process(grap, i, visited,dp));
        }
        System.out.println(res);
    }
    //从i开始所有路只过一遍绕到0的最短路径
    public static int process(int[][] grap, int cur, int v,int[][] dp) {
        if(dp[cur][v] != 0){
            return dp[cur][v];
        }
        if (v == fin) {
            dp[cur][v] = grap[cur][0];
            return grap[cur][0];
        }
        int n = grap.length;
        int res = Integer.MAX_VALUE;
        for (int i = 1; i < n; i++) {
            if (((1 << i) & v) == 0) {
                int vis  = v | (1 << i);
                res = Math.min(res, grap[cur][i] + process(grap, i, vis,dp));
            }
        }
        dp[cur][v] = res;
        return res;
    }
}

P6

题目描述

6.
找零
Z国的货币系统包含面值1元、4元、16元、64元共计4种硬币,以及面值1024元的纸币。现在小Y使用1024元的纸币购买了一件价值为N (0 < N \le 1024)N(0<N≤1024)的商品,请问最少他会收到多少硬币?
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
一行,包含一个数N。
输出描述:
一行,包含一个数,表示最少收到的硬币数。
示例1
输入例子:
200
输出例子:
17
例子说明:
花200,需要找零824块,找12个64元硬币,3个16元硬币,2个4元硬币即可。

思路

力扣原题,简单递归。

代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        int[] coins = new int[] {1, 4, 16, 64};
        int res = process(coins, 1024 - n, 0);
        System.out.println(res);
    }
    public static int process(int[] coins, int rest, int index) {
        if (rest == 0) {
            return 0;
        }
        if (rest < 0) {
            return Integer.MAX_VALUE;
        }
        if (index == coins.length) {
            return rest == 0 ? 0 : Integer.MAX_VALUE;
        }
        int res = Integer.MAX_VALUE;
        for (int zhang = 0; rest - (zhang * coins[index]) >= 0; zhang++) {
            int next = process(coins, rest - (zhang * coins[index]), index + 1);
            if (next != Integer.MAX_VALUE) {
                res = Math.min(zhang + next, res);
            }
        }
        return res;
    }
}

P7

题目描述

7.
机器人跳跃问题
机器人正在玩一个古老的基于DOS的游戏。游戏中有N+1座建筑——从0到N编号,从左到右排列。编号为0的建筑高度为0个单位,编号为i的建筑的高度为H(i)个单位。 

起初, 机器人在编号为0的建筑处。每一步,它跳到下一个(右边)建筑。假设机器人在第k个建筑,且它现在的能量值是E, 下一步它将跳到第个k+1建筑。它将会得到或者失去正比于与H(k+1)与E之差的能量。如果 H(k+1) > E 那么机器人就失去 H(k+1) - E 的能量值,否则它将得到 E - H(k+1) 的能量值。

游戏目标是到达第个N建筑,在这个过程中,能量值不能为负数个单位。现在的问题是机器人以多少能量值开始游戏,才可以保证成功完成游戏?
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
第一行输入,表示一共有 N 组数据.

第二个是 N 个空格分隔的整数,H1, H2, H3, ..., Hn 代表建筑物的高度
输出描述:
输出一个单独的数表示完成游戏所需的最少单位的初始能量
示例1
输入例子:
5
3 4 3 2 4
输出例子:
4
示例2
输入例子:
3
4 4 4
输出例子:
4
示例3
输入例子:
3
1 6 4
输出例子:
3

思路

题意明显可知,当能量值为数组最大值时必定可通过,问所需最少能量。从左向右跳跃,只有数据量限制,可想到二分。

对应思路可参考偷香蕉等类型题目。

注:数据量可能超标,需添加限制。

代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static long max = 0;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        long[] nums = new long[n];
        for(int i = 0;i < n;i++){
            nums[i] = sc.nextLong();
            max = Math.max(max,nums[i]);
        }
        long L = 0;
        long R = max;
        long res = 0;
        while(L <= R){
            long mid = L +((R - L) >> 1) ;
            if(process(nums,mid)){
                res = mid;
                R = mid - 1;
            }else{
                L = mid + 1;
            }
        }
        System.out.println(res);
    }
    public static boolean process(long[] nums,long E){
        for(int i = 0;i < nums.length;i++){
            E += E - nums[i];
            if(E < 0){
                return false;
            }
            if(E >= max){
                return true;
            }
        }
        return true;
    }
}

  • 47
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值