【秋招笔试】2025.08.09美团秋招算法岗机考改编

✅ 秋招备战指南 ✅

💡 学习建议:

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

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

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

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

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

题目一:文档管理系统字符转换

1️⃣:建立字符映射表,维护从原始字符到最终字符的映射关系

2️⃣:每次操作更新映射表,避免重复遍历字符串

3️⃣:最终对原字符串逐字符查表得到结果

难度:中等

这道题目的核心在于理解字符变换的累积效果。通过维护一个全局的字符映射表,我们可以将多次字符串操作转化为映射表的更新,从而避免每次都遍历整个字符串。时间复杂度为 O(52m + n),其中 52 是英文字母的总数。

题目二:智能数据分析平台降维算法

1️⃣:理解PCA算法的数学原理和实现步骤

2️⃣:掌握协方差矩阵的计算和特征分解

3️⃣:实现数据投影、重建和误差计算的完整流程

难度:难

这道题目考查的是机器学习中的经典降维算法PCA的手工实现。需要深入理解线性代数中特征分解的原理,掌握numpy库的使用,以及JSON数据格式的处理。算法涉及多个步骤的数值计算,要求较高的编程实现能力。

题目三:社交网络好友链分析

1️⃣:删除连接相同类型用户的边,保留连接不同类型用户的边

2️⃣:使用 DFS 计算每个连通分量的大小

3️⃣:利用组合数学公式计算每个分量内的兼容路径数

难度:中等

这道题目需要理解图论中连通分量的概念。关键洞察是:删除"无效"边后,每个连通分量内的所有简单路径都满足条件。通过数学公式 k(k+1)/2,我们可以快速计算每个分量的贡献,避免枚举所有路径。

题目四:智能电网配线方案分析

1️⃣:理解问题的数学模型:随机完美匹配生成的2-正则图

2️⃣:运用组合数学推导期望和方差的计算公式

3️⃣:预处理逆元和前缀和,实现高效查询

难度:难

这道题目结合了概率论、组合数学和数论。需要深入理解随机匹配的性质,推导出环数的分布规律,并运用模逆元处理分数运算。通过预处理技术,将单次查询的复杂度优化到 O(1)。

01. 文档管理系统字符转换

问题描述

小毛在开发一套文档管理系统,系统需要支持批量文字格式转换功能。文档中包含长度为 的文本,仅由大小写英文字母组成。

系统提供了 种格式转换操作:

  • 操作类型1:选择两个小写字母 (满足 ),将文本中所有在字母表区间 内的小写字母转换为对应的大写字母。
  • 操作类型2:选择两个大写字母 (满足 ),将文本中所有在字母表区间 内的大写字母转换为对应的小写字母。

请帮助小毛实现这个转换功能,输出经过所有操作后的最终文本。

输入格式

第一行包含两个正整数 ),分别表示文本长度和操作次数。

第二行包含一个长度为 的字符串 ,仅由大小写英文字母组成,表示初始文本。

接下来 行,每行包含一个整数 和两个字符 ,表示一次操作:

  • ,则 为小写字母,且
  • ,则 为大写字母,且

输出格式

输出一行字符串,表示执行完所有操作后得到的最终文本。

样例输入

3 1
abc
1 a c
6 2
aAbBcC
1 a b
2 B C

样例输出

ABC
AAbbcc
样例 解释说明
样例1 初始文本"abc",将区间 的小写字母转换为大写,得到"ABC"
样例2 第一次操作将 内的小写字母转为大写,得到"AABBcC";第二次操作将 内的大写字母转为小写,最终得到"AAbbcc"

数据范围

  • 字符串仅由大小写英文字母组成
  • 操作保证合法

题解

这道题的核心思想是维护一个字符映射表。我们不需要每次操作都遍历整个字符串,而是维护一个从原始字符到最终字符的映射关系。

首先,我们为 52 个英文字母(a-z, A-Z)建立一个映射表,初始时每个字符映射到自己。每次操作相当于在现有映射的基础上再应用一层新的变换。

具体步骤:

  1. 初始化映射表 F,F[i] = i(恒等映射)
  2. 对于每个操作,创建新的映射 G:
    • 遍历所有52个字符
    • 对于每个字符,先通过当前映射 F 得到它现在对应的字符
    • 判断这个字符是否需要根据当前操作进行转换
    • 更新映射关系
  3. 用新映射 G 替换旧映射 F
  4. 最后,对原字符串的每个字符查表得到最终结果

时间复杂度:每次操作需要遍历 52 个字符,总复杂度 ,空间复杂度

这种方法的优势在于,无论字符串多长,每次操作的时间都是常数,避免了重复遍历字符串的开销。

参考代码

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

def solve():
    n, m = map(int, input().split())
    s = input()
    
    # 字符到索引的映射
    def ch_to_idx(c):
        return ord(c) - ord('a') if c.islower() else 26 + ord(c) - ord('A')
    
    # 索引到字符的映射
    def idx_to_ch(i):
        return chr(ord('a') + i) if i < 26 else chr(ord('A') + i - 26)
    
    # 初始化映射表
    mp = list(range(52))
    
    for _ in range(m):
        op, l, r = input().split()
        op = int(op)
        
        # 创建新的映射
        new_mp = [0] * 52
        for i in range(52):
            curr = idx_to_ch(mp[i])  # 当前字符
            
            # 根据操作类型判断是否需要转换
            if op == 1 and curr.islower() and l <= curr <= r:
                curr = curr.upper()
            elif op == 2 and curr.isupper() and l <= curr <= r:
                curr = curr.lower()
            
            new_mp[i] = ch_to_idx(curr)
        
        mp = new_mp
    
    # 应用最终映射
    result = []
    for c in s:
        result.append(idx_to_ch(mp[ch_to_idx(c)]))
    
    print(''.join(result))

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

// 字符转索引
int get_idx(char c) {
    return (c >= 'a' && c <= 'z') ? c - 'a' : 26 + c - 'A';
}

// 索引转字符
char get_char(int idx) {
    return idx < 26 ? char('a' + idx) : char('A' + idx - 26);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    
    // 初始化映射数组
    vector<int> mp(52);
    iota(mp.begin(), mp.end(), 0);
    
    for (int i = 0; i < m; i++) {
        int op;
        char l, r;
        cin >> op >> l >> r;
        
        vector<int> new_mp(52);
        for (int j = 0; j < 52; j++) {
            char curr = get_char(mp[j]);  // 获取当前映射的字符
            
            // 判断是否需要转换
            if (op == 1 && islower(curr) && curr >= l && curr <= r) {
                curr = char(curr - 'a' + 'A');  // 转大写
            } else if (op == 2 && isupper(curr) && curr >= l && curr <= r) {
                curr = char(curr - 'A' + 'a');  // 转小写
            }
            
            new_mp[j] = get_idx(curr);
        }
        mp.swap(new_mp);
    }
    
    // 输出最终结果
    for (char c : s) {
        cout << get_char(mp[get_idx(c)]);
    }
    cout << '\n';
    
    return 0;
}
  • Java
import java.util.*;
import java.io.*;

public class Main {
    // 字符转索引
    private static int getIdx(char c) {
        return Character.isLowerCase(c) ? c - 'a' : 26 + c - 'A';
    }
    
    // 索引转字符
    private static char getChar(int idx) {
        return idx < 26 ? (char)('a' + idx) : (char)('A' + idx - 26);
    }
    
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        String s = br.readLine();
        
        // 初始化映射数组
        int[] mp = new int[52];
        for (int i = 0; i < 52; i++) {
            mp[i] = i;
        }
        
        for (int i = 0; i < m; i++) {
            st = new StringTokenizer(br.readLine());
            int op = Integer.parseInt(st.nextToken());
            char l = st.nextToken().charAt(0);
            char r = st.nextToken().charAt(0);
            
            int[] newMp = new int[52];
            for (int j = 0; j < 52; j++) {
                char curr = getChar(mp[j]);  // 获取当前映射的字符
                
                // 判断是否需要转换
                if (op == 1 && Character.isLowerCase(curr) && curr >= l && curr <= r) {
                    curr = Character.toUpperCase(curr);  // 转大写
                } else if (op == 2 && Character.isUpperCase(curr) && curr >= l && curr <= r) {
                    curr = Character.toLowerCase(curr);  // 转小写
                }
                
                newMp[j] = getIdx(curr);
            }
            mp = newMp;
        }
        
        // 输出最终结果
        StringBuilder result = new StringBuilder();
        for (char c : s.toCharArray()) {
            result.append(getChar(mp[getIdx(c)]));
        }
        System.out.println(result.toString());
    }
}

02. 智能数据分析平台降维算法

问题描述

小柯正在开发一个智能数据分析平台,该平台需要对高维数据进行降维处理以提高分析效率。她选择使用主成分分析(PCA)算法来实现数据压缩。

现在需要你帮助小柯实现一个简化版的PCA算法,具体要求如下:

  1. 数据预处理:对训练数据进行去均值处理
  2. 协方差计算:计算去均值后数据的协方差矩阵
  3. 主成分提取:找到第一主成分(最大特征值对应的特征向量)
  4. 数据重建:将测试数据投影到第一主成分上,然后重建回原始维度
  5. 误差评估:计算每个测试样本重建后的均方误差(MSE)

算法的详细步骤:

  1. 去均值化,其中
  2. 协方差矩阵(使用总体方差,ddof=0)
  3. 求第一主成分:使用 numpy.linalg.eigh 获取特征值和特征向量,选择最大特征值对应的特征向量
  4. 方向标准化:如果第一主成分的首个非零分量为负,则整体乘以 -1
  5. 投影重建
  6. 误差计算

输入格式

标准输入包含一行JSON对象:

{
  "train": [[...], [...], ...],
  "test": [[...], [...], ...]
}

其中:

  • train:训练数据,长度 ,每行长度
  • test:测试数据,任意条数,维度与训练数据相同

输出格式

输出一行JSON数组,包含每个测试样本的MSE值(字符串形式,保留两位小数)。

样例输入

{"train": [[0,0],[0,1],[1,0],[1,1]], "test": [[0.5,0.5],[1.5,1.5]]}

样例输出

["0.00", "0.50"]
样例 解释说明
样例1 训练数据是4个2维点,测试数据是2个点。通过PCA降维重建后,第一个测试点的MSE为0.00,第二个为0.50

数据范围

  • (训练样本数)
  • (特征维度)
  • 所有数据为浮点数,范围

题解

这道题考查的是主成分分析(PCA)算法的核心步骤实现。PCA是一种经典的降维算法,通过找到数据的主要变化方向来压缩数据。

算法的核心思想是:

  1. 中心化数据:消除均值偏移的影响
  2. 计算协方差矩阵:衡量不同维度之间的相关性
  3. 特征分解:找到数据变化最大的方向
  4. 投影重建:在主方向上投影后重建数据

关键实现细节:

  • 使用 numpy.linalg.eigh 而不是 eig,因为协方差矩阵是对称的
  • 特征值按升序排列,所以最大特征值对应的特征向量在最后一列
  • 方向标准化保证结果的唯一性
  • 重建过程就是先投影到低维,再投影回高维的过程

时间复杂度主要由特征分解决定,为 ,其中 是特征维度。

参考代码

  • Python
import sys
import json
import numpy as np

def solve():
    # 读取JSON输入
    data = json.loads(sys.stdin.read().strip())
    
    # 解析训练和测试数据
    train = np.asarray(data["train"], dtype=float)
  

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

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

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

全部评论

相关推荐

投递小鹏汽车等公司10个岗位
点赞 评论 收藏
分享
评论
1
3
分享

创作者周榜

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