【春招笔试】2025.03.15-阿里淘天机考笔试改编题

✅ 春招备战指南 ✅

💡 学习建议:

  • 先尝试独立解题(建议用时:90分钟/套)
  • 对照解析查漏补缺
  • 配套练习题库

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

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

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

淘天2025.03.15题目集

题目一:子数组MEX值统计

1️⃣:利用容斥原理,将子数组按MEX值分类统计

2️⃣:通过计算不包含特定数字的子数组数量,避免直接枚举

3️⃣:时间复杂度O(n),空间复杂度O(n)

难度:中等

题目二:部落联盟探索

1️⃣:使用BFS/DFS找出所有连通分量(亲密部落)

2️⃣:在遍历过程中统计每个连通分量相邻的不同敌对部落

3️⃣:时间复杂度O(n×m),适用于给定的数据范围

难度:中等

题目三:区间乘积求和

1️⃣:将数组按0分段,分别处理每个不含0的段

2️⃣:对于全1的段直接计算,对于其他段枚举起点计算乘积

3️⃣:利用乘积超过10^9即停止计算的特性优化时间复杂度

难度:中等

01. 子数组MEX值统计

问题描述

小兰最近在研究一种特殊的数组统计方法。对于一个整数数组,她定义了一个叫做"MEX"(Minimum EXcluded)的值,即数组中未出现的最小非负整数。例如,数组 的MEX值为0,数组 的MEX值为1。

现在,小兰有一个由 个整数组成的数组 ,其中每个元素的值都在0到2之间(包括0和2)。她想要计算这个数组的所有连续非空子数组的MEX值之和。

连续非空子数组是指从原数组中选取连续的一段元素形成的新数组,且至少包含一个元素。

输入格式

第一行输入一个整数 ,表示数组的长度。

第二行输入 个整数 ,表示数组中的元素。

输出格式

输出一个整数,表示所有连续非空子数组的MEX值之和。

样例输入

3
1 1 0

样例输出

5
样例 解释说明
样例1 长度为1的子数组:[1]的MEX=0,[1]的MEX=0,[0]的MEX=1,总和为0+0+1=1
长度为2的子数组:[1,1]的MEX=0,[1,0]的MEX=2,总和为0+2=2
长度为3的子数组:[1,1,0]的MEX=2,总和为2
所有子数组的MEX值之和为1+2+2=5

数据范围

题解

这道题目要求计算数组所有连续非空子数组的MEX值之和。由于数组中的元素只有0、1、2三种可能,所以子数组的MEX值只可能是0、1、2或3。

我们可以根据MEX的定义,将所有子数组分为四类:

  1. MEX=0:子数组中不包含0
  2. MEX=1:子数组中包含0但不包含1
  3. MEX=2:子数组中包含0和1但不包含2
  4. MEX=3:子数组中同时包含0、1和2

关键思路是:不直接枚举所有子数组(这样复杂度会达到O(n²)),而是统计每种MEX值对应的子数组个数,然后乘以对应的MEX值求和。

具体算法步骤:

  1. 计算数组的所有连续非空子数组总数:
  2. 统计不包含特定数字的子数组个数:
    • 定义函数countNo(x):统计不包含数字x的子数组个数
    • 定义函数countNoPair(x,y):统计不同时包含数字x和y的子数组个数
  3. 计算各类MEX值的子数组个数:
    • MEX=0的子数组个数:cnt_mex0 = countNo(0)
    • MEX=1的子数组个数:cnt_mex1 = countNo(1) - countNoPair(0,1)
    • MEX=2的子数组个数:cnt_mex2 = countNo(2) - countNoPair(0,2) - countNoPair(1,2)
    • MEX=3的子数组个数:cnt_mex3 = total - (cnt0 + cnt1_all + cnt2_all) + (cnt01 + cnt02 + cnt12)
  4. 计算最终答案:ans = 0×cnt_mex0 + 1×cnt_mex1 + 2×cnt_mex2 + 3×cnt_mex3

对于countNo和countNoPair函数,我们可以通过扫描数组,找出连续不包含特定数字的段,然后利用公式 计算这些段中的子数组个数。

时间复杂度:O(n),我们只需要扫描数组常数次。 空间复杂度:O(n),用于存储输入数组。

参考代码

  • Python
import sys
input = lambda:sys.stdin.readline().strip()

def calc_no(arr, x):
    # 统计不包含数字x的子数组数量
    res = 0
    len_seg = 0
    
    for num in arr:
        if num == x:
            # 计算当前段的子数组数量并累加
            res += len_seg * (len_seg + 1) // 2
            len_seg = 0
        else:
            len_seg += 1
    
    # 处理最后一段
    res += len_seg * (len_seg + 1) // 2
    return res

def calc_no_pair(arr, x, y):
    # 统计不同时包含x和y的子数组数量
    res = 0
    len_seg = 0
    
    for num in arr:
        if num == x or num == y:
            # 计算当前段的子数组数量并累加
            res += len_seg * (len_seg + 1) // 2
            len_seg = 0
        else:
            len_seg += 1
    
    # 处理最后一段
    res += len_seg * (len_seg + 1) // 2
    return res

def main():
    n = int(input())
    arr = list(map(int, input().split()))
    
    # 计算所有子数组的总数
    total = n * (n + 1) // 2
    
    # 统计不包含特定数字的子数组数量
    cnt0 = calc_no(arr, 0)  # 不包含0的子数组
    cnt1 = calc_no(arr, 1)  # 不包含1的子数组
    cnt2 = calc_no(arr, 2)  # 不包含2的子数组
    
    # 统计不同时包含两个特定数字的子数组数量
    cnt01 = calc_no_pair(arr, 0, 1)  # 不同时包含0和1的子数组
    cnt02 = calc_no_pair(arr, 0, 2)  # 不同时包含0和2的子数组
    cnt12 = calc_no_pair(arr, 1, 2)  # 不同时包含1和2的子数组
    
    # 计算各类MEX值的子数组个数
    cnt_mex0 = cnt0  # MEX=0:不包含0
    cnt_mex1 = cnt1 - cnt01  # MEX=1:包含0但不包含1
    cnt_mex2 = cnt2 - cnt02 - cnt12  # MEX=2:包含0和1但不包含2
    
    # 使用容斥原理计算MEX=3的子数组个数
    cnt_mex3 = total - (cnt0 + cnt1 + cnt2) + (cnt01 + cnt02 + cnt12)
    
    # 计算最终答案
    ans = 1 * cnt_mex1 + 2 * cnt_mex2 + 3 * cnt_mex3
    print(ans)

if __name__ == "__main__":
    main()
  • Cpp
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;

// 统计不包含数字x的子数组数量
ll count_no(const vector<int>& arr, int x) {
    ll res = 0;
    ll seg_len = 0;
    
    for (int val : arr) {
        if (val == x) {
            // 计算当前段的子数组数量并累加
            res += seg_len * (seg_len + 1) / 2;
            seg_len = 0;
        } else {
            seg_len++;
        }
    }
    
    // 处理最后一段
    res += seg_len * (seg_len + 1) / 2;
    return res;
}

// 统计不同时包含x和y的子数组数量
ll count_pair(const vector<int>& arr, int x, int y) {
    ll res = 0;
    ll seg_len = 0;
    
    for (int val : arr) {
        if (val == x || val == y) {
            // 计算当前段的子数组数量并累加
            res += seg_len * (seg_len + 1) / 2;
            seg_len = 0;
        } else {
            seg_len++;
        }
    }
    
    // 处理最后一段
    res += seg_len * (seg_len + 1) / 2;
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<int> arr(n);
    
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    
    // 计算所有子数组的总数
    ll total = (ll)n * (n + 1) / 2;
    
    // 统计不包含特定数字的子数组数量
    ll cnt0 = count_no(arr, 0);  // 不包含0的子数组
    ll cnt1 = count_no(arr, 1);  // 不包含1的子数组
    ll cnt2 = count_no(arr, 2);  // 不包含2的子数组
    
    // 统计不同时包含两个特定数字的子数组数量
    ll cnt01 = count_pair(arr, 0, 1);  // 不同时包含0和1的子数组
    ll cnt02 = count_pair(arr, 0, 2);  // 不同时包含0和2的子数组
    ll cnt12 = count_pair(arr, 1, 2);  // 不同时包含1和2的子数组
    
    // 计算各类MEX值的子数组个数
    ll mex0 = cnt0;  // MEX=0:不包含0
    ll mex1 = cnt1 - cnt01;  // MEX=1:包含0但不包含1
    ll mex2 = cnt2 - cnt02 - cnt12;  // MEX=2:包含0和1但不包含2
    
    // 使用容斥原理计算MEX=3的子数组个数
    ll mex3 = total - (cnt0 + cnt1 + cnt2) + (cnt01 + cnt02 + cnt12);
    
    // 计算最终答案
    ll ans = 1 * mex1 + 2 * mex2 + 3 * mex3;
    cout << ans << "\n";
    
    return 0;
}
  • Java
import java.util.*;

public class Main {
    // 统计不包含数字x的子数组数量
    public static long countNo(int[] arr, int x) {
        long res = 0;
        long segLen = 0;
        
        for (int val : arr) {
            if (val == x) {
                // 计算当前段的子数组数量并累加
                res += segLen * (segLen + 1) / 2;
                segLen = 0;
            } else {
                segLen++;
            }
        }
        
        // 处理最后一段
        res += segLen * (segLen + 1) / 2;
        return res;
    }
    
    // 统计不同时包含x和y的子数组数量
    public static long countPair(int[] arr, int x, int y) {
        long res = 0;
        long segLen = 0;
        
        for (int val : arr) {
            if (val == x || val == y) {
                // 计算当前段的子数组数量并累加
                res += segLen * (segLen + 1) / 2;
                segLen = 0;
            } else {
                segLen++;
            }
        }
        
        // 处理最后一段
        res += segLen * (segLen + 1) / 2;
        return res;
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        
        // 计算所有子数组的总数
        long total = (long)n * (n + 1) / 2;
        
        // 统计不包含特定数字的子数组数量
        long cnt0 = countNo(arr, 0);  // 不包含0的子数组
        long cnt1 = countNo(arr, 1);  // 不包含1的子数组
        long cnt2 = countNo(arr, 2);  // 不包含2的子数组
        
        // 统计不同时包含两个特定数字的子数组数量
        long cnt01 = countPair(arr, 0, 1);  // 不同时包含0和1的子数组
        long cnt02 = countPair(arr, 0, 2);  // 不同时包含0和2的子数组
        long cnt12 = countPair(arr, 1, 2);  // 不同时包含1和2的子数组
        
        // 计算各类MEX值的子数组个数
        long mex0 = cnt0;  // MEX=0:不包含0
        long mex1 = cnt1 - cnt01;  // MEX=1:包含0但不包含1
        long mex2 = cnt2 - cnt02 - cnt12;  // MEX=2:包含0和1但不包含2
        
        // 使用容斥原理计算MEX=3的子数组个数
        long mex3 = total - (cnt0 + cnt1 + cnt2) + (cnt01 + cnt02 + cnt12);
        
        // 计算最终答案
        long ans = 1 * mex1 + 2 * mex2 + 3 * mex3;
        System.out.println(ans);
        
        sc.close();
    }
}

02. 部落联盟探索

问题描述

小基是一位人类学家,正在研究一个古老国家的部落分布。这个国家的领土可以看作一个 列的矩阵,每个单元格中都居住着一个部落,用小写字母表示。

小基发现,同一个部族的成员如果居住在相邻的单元格中,他们会形成一个"亲密部落"。具体来说,如果两个单元格 满足以下条件:

  1. 它们包含相同的部族字母(
  2. 它们在地理上相邻(,即上下左右四个方向)

那么这两个单元格属于同一个亲密部落。

现在,小基想要分析每个单元格所在的亲密部落与多少个不同的敌对部落(非本部族的部落)相邻。两个部落相邻是指存在一个单元格属于第一个部落,另一个单元格属于第二个部落,且这两个单元格在地理上相邻。

请帮助小基完成这项研究。

输入格式

第一行输入两个整数 ,表示矩阵的行数和列数。

接下来 行,每行包含一个长度为 的字符串,由小写字母组成,表示每个单元格中的部落。

输出格式

输出 行,每行 个整数,用空格分隔。第 行第 个整数表示位于 的单元格所在的亲密部落与多少个不同的敌对部落相邻。

样例输入

3 3
baa
aab
cac

样例输出

1 2 2
2 2 2
1 2 2
样例 解释说明
样例1 以位置(1,2)的'a'部落为例,它所在的亲密部落包含5个单元格:(1,2)、(1,3)、(2,1)、(2,2)和(3,2)。
这个亲密部落与'b'和'c'两个不同的敌对部落相邻,所以答案是2。

数据范围

  • 输入中的字符均为小写字母

题解

这道题目要求我们找出每个单元格所在的亲密部落与多少个不同的敌对部落相邻。

首先,我们需要理解什么是"亲密部落":同一个字母(部族)的相邻单元格组成一个亲密部落。这实际上就是图论中的连通分量概念。

解题思路如下:

  1. 使用广度优先搜索(BFS)或深度优先搜索(DFS)找出所有的连通分量(亲密部落)
  2. 对于每个连通分量,统计与之相邻的不同敌对部落(不同字母)的数量
  3. 将每个单元格映射到其所在的连通分量,输出相应的敌对部落数量

具体实现步骤:

  1. 创建一个二维数组comp,用于标记每个单元格所属的连通分量编号
  2. 创建一个数组enemyCount,记录每个连通分量相邻的不同敌对部落数量
  3. 使用BFS遍历矩阵:
    • 对于每个未访问的单元格,以它为起点进行BFS
    • 将同一部族且相邻的单元格标记为同一个连通分量
    • 在BFS过程中,记录与当前连通分量相邻的不同部族
  4. 最后,输出每个单元格所在连通分量的敌对部落数量

时间复杂度分析:

  • 每个单元格最多被访问一次,所以BFS的时间复杂度是O(n*m)
  • 对于每个单元格,我们需要检查其四个相邻位置,这是常数时间
  • 总体时间复杂度为O(n*m)

空间复杂度:

  • 需要O(n*m)的空间来存储连通分量标记和队列
  • 总体空间复杂度为O(n*m)

这个解法在给定的数据范围(n,m≤500)下是高效的。

参考代码

  • Python
import sys
input = lambda:sys.stdin.readline().strip()
from collections import deque

def main():
    # 读取输入
    n, m = map(int, input().split())
    grid = [input() for _ in range(n)]
    
    # 初始化连通分量标记数组,-1表示未访问
    comp = [[-1 for _ in range(m)] for _ in range(n)]
    # 存储每个连通分量的敌对部落数量
    enemy_cnt = []
    
    # 四个方向:上、下、左、右
    dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    
    comp_id = 0
    # 使用BFS找出所有连通分量
    for i in range(n):
        for j in range(m):
            if comp[i][j] == -1:  # 未访问过的单元格
                tribe = grid[i][j]  # 当前部族
                enemies = set()  # 存储相邻的敌对部落
                
                # BFS
                q = deque([(i, j)])
                comp[i][j] = comp_id
                
                while q:
                    x, y = q.popleft()
                    
                    # 检查四个方向
                    for dx, dy in dirs:
                       

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

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

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

全部评论

相关推荐

关于客户端行业:入行需谨慎在当今这个科技飞速发展的时代,新兴行业如璀璨繁星般不断涌现,吸引着无数怀揣梦想与热情的年轻人投身其中。客户端领域,曾几何时也是众人瞩目的焦点,然而如今,我却想真诚地劝诫各位,入行客户端需慎重考虑。曾经,客户端行业宛如一片充满宝藏的新大陆,吸引着大批开拓者。那时候,市场需求旺盛,似乎只要有一款稍有特色的客户端产品推出,就能收获大量用户,获得可观的收益。各大公司纷纷布局,投入巨额资金进行研发和推广,行业内一片热火朝天的景象。开发人员备受尊崇,薪资待遇优厚,职业前景看似一片光明。但如今,客户端行业的辉煌已悄然褪色,现实的残酷正无情地摆在眼前。首先,市场竞争达到了白热化的程度。随着时间的推移,各类客户端应用层出不穷,几乎涵盖了生活的方方面面,市场已然趋近饱和。新的客户端想要在这片拥挤的红海之中崭露头角,难度堪比登天。海量的同类产品相互厮杀,用户的选择众多,导致新客户端获取用户的成本急剧攀升。为了吸引哪怕一小部分用户,企业往往需要投入巨额的营销费用,可最终的效果却常常不尽人意。许多创业团队满怀希望地进入这个领域,却在激烈的竞争中折戟沉沙,血本无归。其次,技术更新换代的速度令人目不暇接。客户端行业是一个典型的技术驱动型领域,新技术、新框架不断涌现。今天流行的技术,或许明天就会被淘汰。这就要求从业者必须时刻保持学习的状态,不断更新自己的知识体系,以跟上行业的发展步伐。对于初入行业的新人来说,不仅要掌握扎实的基础知识,还要花费大量的时间和精力去学习最新的技术,压力之大可想而知。而且,即便你努力跟上了技术的节奏,也不能保证你的技能就能一直适应市场的需求。一旦技术方向发生转变,之前的努力可能就会付诸东流,面临重新学习的困境。再者,客户端行业的盈利模式日益复杂且不稳定。过去,广告投放和付费会员是常见的盈利方式,但如今,随着用户对广告的抵触情绪越来越高,广告效果大打折扣,广告收入也随之减少。而付费会员模式,在竞争激烈的市场环境下,用户对于付费的意愿普遍较低,想要培养用户的付费习惯并非易事。此外,政策法规的不断变化也给行业的盈利带来了诸多不确定性。一些原本可行的盈利手段,可能因为政策的调整而被迫终止,企业不得不重新寻找盈利途径,这无疑增加了运营的风险。另外,客户端行业的工作强度极大,对从业者的身心健康造成了严重的挑战。为了赶项目进度、修复漏洞、应对紧急情况,加班加点成为了家常便饭。长期处于这种高强度的工作状态下,身体很容易出现各种问题,精神压力也会与日俱增。许多从业者在年纪轻轻时就患上了各种职业病,生活质量严重下降。而且,由于工作占据了大量的时间,个人的社交生活和家庭关系也往往受到影响,导致身心疲惫。所以,综合以上种种因素,客户端行业如今已不再是那个充满美好憧憬的理想之地。如果你还在考虑是否要踏入这个行业,希望你能充分了解其中的艰辛与风险,慎重做出决定。人生的选择至关重要,有时候,避开看似诱人实则布满荆棘的道路,也是一种智慧。#客户端#
投递新大陆科技集团等公司6个岗位
点赞 评论 收藏
分享
在AI产品设计中,Agent(智能体)和Workflow(工作流)是两种核心范式,分别代表了智能化与流程化的不同方向。要从以下方面展开分析:一、核心区别二、Agent的核心特性三、Workflow的设计逻辑四、协同关系五、选择决策要点具体来说:1.&nbsp;定义与功能:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:自主决策实体,能感知环境、做出决策并执行动作&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:预定义的任务序列,按照固定流程执行2.&nbsp;执行方式:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:动态响应,根据输入和环境变化自主调整行为&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:线性执行,严格遵循预设步骤3.&nbsp;灵活性:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:具备学习适应能力,可处理未知情况&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:静态结构,变更需要手动修改流程4.&nbsp;决策能力:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:内置决策逻辑,可实时评估选择最佳路径&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:无自主决策,完全依赖流程设计5.&nbsp;复杂度:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:通常包含状态记忆和目标导向行为&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:侧重任务编排和顺序控制6.&nbsp;典型应用:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:聊天机器人、自动驾驶、游戏AI&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:数据处理流水线、审批系统、CI/CD流程7.&nbsp;错误处理:&nbsp;&nbsp;&nbsp;-&nbsp;Agent:可自主尝试恢复或寻找替代方案&nbsp;&nbsp;&nbsp;-&nbsp;Workflow:依赖预设的错误处理分支总结:Agent是“大脑”,Workflow是“骨架”,二者结合可构建从灵活到稳定的AI应用光谱。未来方向是通过低代码平台降低两者融合门槛,实现智能化与自动化的统一。 #牛客激励计划#&nbsp;&nbsp;#聊聊我眼中的AI#&nbsp;&nbsp;#产品经理# #牛客AI配图神器#
点赞 评论 收藏
分享
评论
1
5
分享

创作者周榜

更多
牛客网
牛客企业服务