【笔试刷题】字节-2026.04.19-改编真题

✅ 春招备战指南 ✅

💡 学习建议:

  • 先尝试独立解题
  • 对照解析查漏补缺

🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。

🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力

🤖 内容包含AI辅助生成,题解和代码均经过多轮验证,有问题欢迎评论

🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的

春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗

字节-2026.04.19

字节-2026.04.19

题目一:小兰的走廊按钮

这一题的关键在于先固定按钮持续时间,再去问“最少需要按几次”。固定后可以直接从左往右贪心覆盖第一个还没处理的关闭门,再利用可行性的单调性二分答案。

难度:Mid

题目二:小兰的印章网格

判断过程要围绕“一个合法图形一定能拆成若干个彼此不接触的 红块”展开。先找完整红块,再判相邻冲突,最后做一遍覆盖核验,三个步骤缺一不可。

难度:Mid

题目三:小兰的尾零字典序

操作虽然作用在整个数组上,但变化的是因子 的位置分布。字典序比较优先看前缀,所以应该从左到右尽量把前面的因子 拿走,并把代价统一丢到最后一个位置。

难度:Mid

题目四:小兰的第二大池化

题意就是标准模拟,没有隐藏状态。按层处理所有 小块,每块留下第二大值,直到矩阵缩成一个数即可。

难度:Low

1. 小兰的走廊按钮

问题描述

小兰需要沿着一条长度为 的走廊从左走到右,途中会依次经过编号为 的门。

扇门的状态用 表示:

  • ,表示这扇门原本就是开着的。
  • ,表示这扇门原本是关着的。

她手里有一个按钮,最多可以按下 次。

如果在准备通过某一扇门时按下按钮,并把持续时间设为 ,那么从当前门开始,接下来连续 扇门都会临时视为打开状态。由于每通过一扇门恰好花费 秒,所以这个持续时间恰好对应连续 扇门。

小兰想把持续时间设得尽量短。请你求出,为了顺利通过全部门,最小的非负整数 是多少。

输入格式

第一行输入一个整数 ,表示测试数据组数。

对于每组数据:

  • 第一行输入两个整数 ,分别表示门的数量和按钮最多使用次数。
  • 第二行输入 个整数 ,表示每扇门的初始状态。

输出格式

对于每组数据,输出一行一个整数,表示最小的可行持续时间

样例输入

3
5 1
0 1 1 0 1
7 2
1 0 1 1 0 1 0
4 2
0 0 0 0

样例输出

4
3
0

样例输入 2

1
6 2
1 1 0 0 1 1

样例输出 2

2

数据范围

  • 保证所有测试数据中

样例说明

样例 解释说明
样例 1 组数据里,若 ,从第 扇门按下按钮后只能覆盖到第 扇门,第 扇门仍然无法通过,所以最小值是 。第 组数据中所有门本来就打开,按钮一次都不用按,答案为
样例 2 可以在第 扇门和第 扇门前各按一次按钮,持续时间取 就能覆盖全部关闭门。

题解

先把持续时间 看成已经确定,问题会变成:

“至少要按几次按钮,才能让所有关闭门都被覆盖?”

1. 固定 后,最优策略是什么

从左到右扫描所有门。

如果当前门本来就是开的,或者已经被前一次按钮覆盖到了,那么直接继续往后走。

如果扫到的是当前最靠左、还没有被覆盖的关闭门,那么这一次按钮必须在这里按下。

原因很简单:

  • 按得更晚,当前这扇门已经过不去。
  • 按得更早,也不会让左边变得更好,因为左边已经处理完了。

所以固定 时,可以直接贪心:

  • 遇到第一个未覆盖的关闭门,就按一次按钮。
  • 这一按会覆盖当前位置开始的连续 扇门。
  • 然后把指针跳到覆盖区间之后,继续扫描。

这样扫完整条走廊,得到的就是固定 时所需的最少按键次数。

2. 为什么可以二分答案

如果某个持续时间 已经可行,那么任何更大的持续时间也一定可行。

因为每次按钮能覆盖的门只会更多,不会更少。

这说明“是否能在不超过 次内通过全部门”对 具有单调性,可以直接二分最小可行值。

3. 边界怎么处理

如果整条走廊里没有一扇关闭门,那么不需要按按钮,答案就是

否则答案一定在 之间,直接在这个区间上二分即可。

4. 复杂度分析

二分会做 次检查。

每次检查只需要线性扫一遍门,复杂度是

所以单组数据总复杂度为 ,可以通过题目范围。

  • Python
import sys

input = lambda: sys.stdin.readline().strip()

def chk(arr, x):
    n = len(arr)
    cnt = 0
    i = 0
    while i < n:
        # 原本就打开的门可以直接通过。
        if arr[i] == 0:
            i += 1
            continue

        # 这是最靠左、还没被覆盖的关闭门,
        # 这一按必须从这里开始。
        cnt += 1
        i += x
    return cnt

def solve():
    t = int(input())
    out = []

    for _ in range(t):
        n, k = map(int, input().split())
        arr = list(map(int, input().split()))

        # 全是开门时,持续时间取 0 就够了。
        if sum(arr) == 0:
            out.append("0")
            continue

        l, r = 1, n
        ans = n
        while l <= r:
            mid = (l + r) // 2
            if chk(arr, mid) <= k:
                ans = mid
                r = mid - 1
            else:
                l = mid + 1

        out.append(str(ans))

    print("\n".join(out))

if __name__ == "__main__":
    solve()
  • Cpp
#include <bits/stdc++.h>
using namespace std;

int need(const vector<int>& arr, int x) {
    int n = (int)arr.size();
    int cnt = 0;

    for (int i = 0; i < n; ) {
        // 开门可以直接通过。
        if (arr[i] == 0) {
            ++i;
            continue;
        }

        // 这是最靠左的未覆盖关闭门,
        // 按钮必须从这里开始生效。
        ++cnt;
        i += x;
    }

    return cnt;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;

        vector<int> arr(n);
        int sum = 0;
        for (int i = 0; i < n; ++i) {
            cin >> arr[i];
            sum += arr[i];
        }

        // 没有关闭门时,答案就是 0。
        if (sum == 0) {
            cout << 0 << "\n";
            continue;
        }

        int l = 1, r = n, ans = n;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (need(arr, mid) <= k) {
                ans = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }

        cout << ans << "\n";
    }
    return 0;
}
  • Java
import java.io.*;
import java.util.*;

public class Main {
    static int need(int[] arr, int x) {
        int n = arr.length;
        int cnt = 0;

        for (int i = 0; i < n; ) {
            // 开门不需要额外处理。
            if (arr[i] == 0) {
                i++;
                continue;
            }

            // 当前最靠左的未覆盖关闭门,
            // 只能从这里开始按按钮。
            cnt++;
            i += x;
        }

        return cnt;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine().trim());
        StringBuilder out = new StringBuilder();

        while (t-- > 0) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int n = Integer.parseInt(st.nextToken());
            int k = Integer.parseInt(st.nextToken());

            st = new StringTokenizer(br.readLine());
            int[] arr = new int[n];
            int sum = 0;
            for (int i = 0; i < n; i++) {
                arr[i] = Integer.parseInt(st.nextToken());
                sum += arr[i];
            }

            // 全部已经打开时,最优持续时间就是 0。
            if (sum == 0) {
                out.append(0).append('\n');
                continue;
            }

            int l = 1, r = n, ans = n;
            while (l <= r) {
                int mid = (l + r) >>> 1;
                if (need(arr, mid) <= k) {
                    ans = mid;
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }

            out.append(ans).append('\n');
        }

        System.out.print(out);
    }
}

2. 小兰的印章网格

问题描述

小兰有一枚固定大小为 的印章。每按一次印章,对应的 子矩阵都会被全部染成红色。

她在一张 的网格上按下了若干次印章,并且整个过程始终满足两条规则:

  • 每个格子至多被染色一次。
  • 任意两个红色 块都不能相邻。这里的相邻指两块红色区域只要共用一条边,或者只共用一个角,都算相邻。

现在只知道最终得到的矩阵:

  • * 表示这个格子是红色。
  • . 表示这个格子没有被染色。

请你判断,这个最终矩阵是否可能由上述过程得到。

共有 组询问。

输入格式

第一行输入一个整数 ,表示询问组数。

对于每组数据:

  • 第一行输入两个整数 ,表示网格的行数和列数。
  • 接下来输入 行,每行是一个长度为 的字符串,只包含 *.

输出格式

对于每组数据输出一行。

  • 如果这个矩阵合法,输出 Yes
  • 否则输出 No

样例输入

3
6 7
**...**
**...**
.......
.......
**...**
**...**
4 6
**....
**....
..**..
..**..
3 4
.**.
.**.
....

样例输出

Yes
No
Yes

样例输入 2

2
1 5
.*.*.
2 3
***
***

样例输出 2

No
No

数据范围

样例说明

样例 解释说明
样例 1 组数据里有四个彼此完全分开的 红块,所以合法。第 组数据中两块红色区域会在角上接触,不满足题意。第 组数据恰好是一枚单独的印章。
样例 2 组数据连一枚完整的 印章都放不下。第 组数据会产生两个互相重叠的候选印章位置,也不合法。

题解

这一题只需要判断最终图形能否拆成若干块互不接触的 红块。

1. 合法印章留下来的样子

一次印章会把一个完整的 区域染红。

所以只要某个图形是合法答案,那么其中每一个红色格子都必须属于某个完整的 红块。

这给出了第一步做法:

  • 枚举所有左上角位置。
  • 找出所有四个格子全为 * 红块。

2. 怎么判断这些红块会不会互相接触

设两个候选红块的左上角分别是

由于每个红块固定都是 ,只要同时满足:

这两块就一定会出现下面某一种情况:

  • 重叠;
  • 共边;
  • 只共角。

这些情况全都不允许。

反过来,只要行差或列差有一个超过 ,两块之间就至少隔开了一整行或一整列,不会互相接触。

因此可以把每个候选红块的左上角记下来,再检查周围 的左上角范围里有没有别的候选块。

3. 为什么最后还要做一次覆盖检查

找到候选红块并且确认它们彼此不接触后,事情还没结束。

因为原矩阵里可能还会残留某些 *,它们并不属于任何完整的 红块。

这类格子不可能通过合法印章得到。

所以还需要把所有候选红块覆盖到一张辅助表上,再扫描原矩阵:

  • 如果某个 * 没有被覆盖到,答案就是 No
  • 只有所有 * 都恰好属于某个合法红块时,答案才是 Yes

4. 特殊情况

如果 ,连一枚印章都放不下。

这时只有“整个矩阵全是 .”才合法,只要出现一个 * 就一定不合法。

5. 复杂度分析

枚举所有 子矩阵是

每个候选块只检查常数个相邻位置,最后覆盖检查也是

所以单组数据总复杂度为

  • Python
import sys

input = lambda: sys.stdin.readline().strip()

def solve():
    t = int(input())
    out = []

    for _ in range(t):
        n, m = map(int, input().split())
        g = [input() for _ in range(n)]

        # 行或列不足 2 时,根本放不下印章。
        if n < 2 or m < 2:
            ok = True
            for i in range(n):
                for j in range(m):
                    if g[i][j] == '*':
                        ok = False
            out.append("Yes" if ok else "No")
            continue

        pos = []
        has = [[False] * m for _ in range(n)]

        # 先找出所有完整的 2x2 红块。
        for i in range(n - 1):
            for j in range(m - 1):
                if g[i][j] == '*' and g[i + 1][j] == '*' and \
                   g[i][j + 1] == '*' and g[i + 1][j + 1] == '*':
                    pos.append((i, j))
                    has[i][j] = True

        ok = True

        # 两个左上角只要同时落在 5x5 邻域内,
        # 两块红色区域就一定会接触。
        for x, y in pos:
            for dx in range(-2, 3):
                for dy in range(-2, 3):
                    if dx == 0 and dy == 0:
                        continue
                    nx = x + dx
                    ny = y + dy
                    if 0 <= nx < n and 0 <= ny < m and has[nx][ny]:
                        ok = False
                        break
                if not ok:
                    break
            if not ok:
                break

        if not ok:
            out.append("No")
            continue

        cov = [[False] * m for _ in range(n)]
        for x, y in pos:
            for dx in range(2):
                for dy in range(2):
                    cov[x + dx][y + dy] = True

        # 每个红格都必须被某个合法印章覆盖到。
        for i in range(n):
            for j in range(m):
                if g[i][j] == '*' and not cov[i][j]:
                    ok = False
                    break
            if not ok:
                break

        out.append("Yes" if ok else "No")

    print("\n".join(out))

if __name__ == "__main__":
    solve()
  • Cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        vector<string> g(n);
        for (int i = 0; i < n; ++i) {
            cin >> g[i];
        }

        // 尺寸太小时,合法情况只能是全白矩阵。
        if (n < 2 || m < 2) {
            bool ok = true;
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < m; ++j) {
          

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

互联网刷题笔试宝典 文章被收录于专栏

互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

全部评论

相关推荐

3.19申请4.15笔试,200分及格分,240多通过4.23早上专业一面&nbsp;&nbsp;1小时左右自我介绍笔试复盘(自己提前准备,两道编程题错在哪里,为什么会出错自己考完笔试就可以先复盘一下)项目拷打(这方面问的非常细,包括项目的成员构成,整个项目的介绍,对于使用到的技术的了解程度以及假设出现了某个问题应该怎么改进。我的项目距离现在有一段时间了,一些比较细节的东西记不太清楚了,不过运气比较好,面试官提到的大多数问题都是我当时做项目的时候有遇到或者思考过的,所以印象相对深刻)一些AI相关的八股(AIagent)一道手撕题(后来我自己复盘发现我的算法并不完美,但是能够解释测例,也还凑合)4.23下午主管面试&nbsp;&nbsp;30分钟左右自我介绍项目介绍(问的比早上要深,包括使用的技术栈和对其它技术栈有多少了解)对前沿AI发展和AI模型框架的认识其它内容的了解,比赛,居住地,期望入职时间之类的总结:感觉华为比较注重就是实践方面,所以项目经历比较丰富或者对项目的各个方面了解比较深的同学是比较有优势的。笔试方面多刷题应该问题不大,我备考的时候牛客上所有套题基本都刷了两三遍(刷到麻了);面试的话应该就着重项目吧,使用的技术、算法原理什么的要多了解一点然后面试表现得自然一些问题应该不大;八股有,但是基本上点到为止,而且偏机器学习和前沿AI相关的多。主管面我当时是比较紧张,不过主管人很好,整个过程基本上是以一个交流分享的状态进行的。后续是怎么个流程,这样子是能成功入职了还是有其它环节我暂时也不清楚了,但是自己能把握的部分已经全部通过了,剩下的看造化了。希望能对各位求职有帮助。
查看7道真题和解析
点赞 评论 收藏
分享
03-31 09:40
门头沟学院 Java
刷到这个话题,突然就停下了敲代码的手。作为刚实习三个月的后端开发,vibe&nbsp;coding&nbsp;早就不是什么&nbsp;“高大上的项目创作”,而是我每天下班之后,在出租屋里给自己留的最后一点编程的快乐。白天在公司写代码,全是条条框框。要严格遵守团队的开发规范,要过同事的&nbsp;code&nbsp;review,要写全单元测试,要考虑线上性能,要应对没完没了的需求变更。写的永远是重复的&nbsp;CRUD,改的永远是测不完的&nbsp;bug,开的永远是没营养的会,敲键盘的时候,心里想的全是&nbsp;“别出&nbsp;bug、别被&nbsp;mentor&nbsp;骂、别耽误提测”。只有晚上回到出租屋,打开电脑,进入&nbsp;vibe&nbsp;coding&nbsp;的状态,才觉得自己是真的在写代码,而不是完成任务。不用管什么开发规范,不用管什么架构设计,不用管什么性能优化,想怎么写就怎么写,想加什么功能就加什么功能,哪怕代码写得再糙,哪怕只有自己能用,哪怕写完玩十分钟就腻了,也没关系。最开始写的第一个&nbsp;vibe&nbsp;coding&nbsp;小玩意,是个打卡提醒的&nbsp;Python&nbsp;脚本。公司是弹性打卡,早来早走、晚来晚走,我总是忙起来就忘了下班时间,经常免费加班半小时才反应过来。就花了半个多小时,写了个挂在后台的小脚本,到了下班时间就弹全屏提醒,还能自动统计每天的打卡时长,算加班了多久,不用再对着打卡表掰着手指头算。这个脚本没有&nbsp;UI,没有打包,甚至连异常处理都只写了最基础的,可我用到现在,每天下班都靠它提醒,比手机闹钟好用一百倍。后来又写了个摸鱼刷题的小工具。秋招要刷算法题,上班总打开牛客网页,怕被路过的&nbsp;leader&nbsp;看见,就用&nbsp;Java&nbsp;写了个最小化的桌面小程序,挂在屏幕角落,每隔一小时弹一道&nbsp;LeetCode&nbsp;简单题,写完就能收起来,不影响写业务代码,也不会被人发现。周末闲着没事的时候,就更放飞了。有次周六下雨,没法出门,就在出租屋里跟着感觉,用&nbsp;Swing&nbsp;写了个贪吃蛇小游戏。没有什么复杂的玩法,就是最基础的上下左右吃豆子,甚至连碰撞检测都写得很糙,可我对着这个小游戏,改颜色、改速度、加无敌模式,折腾了整整一下午,写完之后自己玩了十分钟就腻了,可写代码的那一下午,是我实习以来最放松的时刻。不用管需求,不用管评审,不用管上线,不用为任何人负责,代码只需要取悦我自己。我也试过跟着网上的教程,想写个完整的个人博客,搭好了&nbsp;SpringBoot+Vue&nbsp;的框架,写了登录接口,可写着写着就觉得没意思了&nbsp;——&nbsp;这和白天在公司写业务代码有什么区别?反而没了&nbsp;vibe&nbsp;coding&nbsp;的快乐,最后这个项目就扔在&nbsp;GitHub&nbsp;里,再也没动过。现在我终于明白,vibe&nbsp;coding&nbsp;对我这种实习程序员来说,从来不是为了做出什么牛逼的项目,也不是为了写进简历里加分,就是在被工作磨掉对编程的热情的时候,给自己找回来一点最开始学代码的快乐。大一的时候,第一次用&nbsp;C&nbsp;语言写出个&nbsp;Hello&nbsp;World,都能开心半天;第一次写出个简单的计算器,能跟室友炫耀好久。那时候写代码,没有&nbsp;KPI,没有&nbsp;bug&nbsp;追责,没有需求变更,就是单纯的觉得好玩、有意思。而&nbsp;vibe&nbsp;coding,就是让我在实习的兵荒马马里,重新找回这种快乐的方式。它不用很复杂,不用很完美,甚至不用写完。只要写的那一刻,我是开心的,就够了。
你都用vibe codi...
点赞 评论 收藏
分享
04-22 13:08
门头沟学院 HTML5
Data_Seven:真不知道这些企业哪来的成就感
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务