美团笔试 美团算法笔试 0809
笔试时间:2025年8月9日
往年笔试合集:
第一题
小美有一个长度为n,仅由大小写英文字母组成的字符串s。需执行m次操作: 操作类型op=1时,给定两个小写字母letter1、letter2(满足letter1 ≤ letter2), 将字符串中所有位于字母表中[letter1, letter2]区间的小写字母转为对应大写字母; 操作类型op=2时,给定两个大写字母letter1、letter2(满足letter1 ≤ letter2), 将字符串中所有位于字母表中[letter1, letter2]区间的大写字母转为对应小写字母。
输入描述
一行输入两个整数n, m (1 ≤ n,m ≤ 2×10^5),分别表示字符串长度和操作次数; 一行输入长度为n的字符串s; 接下来m行,每行输入整数op和两个字符letter1、letter2(op=1时为小写且letter1≤letter2;op=2时为大写且letter1≤letter2 )。
输出描述
执行完所有操作后得到的最终字符串。
样例输入
3 1
abc
1 a c
样例输出
ABC
说明: 在此样例中,初始字符串"abc",将区间[a,c]的小写字母统一转换成大写,得到"ABC"。
参考题解
用 index_map 维护 52 个字母(0..25 表示 'a'..'z',26..51 表示 'A'..'Z')的复合映射。 读到操作: op == 1:把当前映射到小写区间 [lo, hi] 的项转成对应的大写:index_map[i] = cur + 26。 op == 2:把当前映射到大写区间 [lo, hi] 的项转回小写:index_map[i] = v(其中 v = cur - 26)。 因为每次都基于 index_map 的当前值改,所以天然完成函数的复合。 最后逐字符用 index_map 取最终目标码位,组合成 out 并输出。整体复杂度 O(52*num_ops + n)。
C++:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
int main() {
string line;
getline(cin, line);
istringstream iss(line);
int n, num_ops;
string text;
iss >> n >> num_ops >> text;
// 0..25 -> 'a'..'z',26..51 -> 'A'..'Z'
vector<int> index_map(52);
for (int i = 0; i < 52; ++i) {
index_map[i] = i;
}
for (int op_idx = 0; op_idx < num_ops; ++op_idx) {
int op;
char lch, rch;
cin >> op >> lch >> rch;
if (op == 1) { // 小写区间转大写
int lo = lch - 'a';
int hi = rch - 'a';
for (int i = 0; i < 52; ++i) {
int cur = index_map[i];
if (cur < 26 && lo <= cur && cur <= hi) {
index_map[i] = cur + 26;
}
}
} else { // 大写区间转小写
int lo = lch - 'A';
int hi = rch - 'A';
for (int i = 0; i < 52; ++i) {
int cur = index_map[i];
if (cur >= 26) {
int v = cur - 26;
if (lo <= v && v <= hi) {
index_map[i] = v;
}
}
}
}
}
string out;
for (char c : text) {
int t;
if (c >= 'a' && c <= 'z') {
t = index_map[c - 'a'];
} else {
t = index_map[26 + (c - 'A')];
}
if (t < 26) {
out += (char)('a' + t);
} else {
out += (char)('A' + (t - 26));
}
}
cout << out << endl;
return 0;
}
Java:
import java.util.Scanner;
public class CaseTransformer {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int numOps = scanner.nextInt();
String text = scanner.next();
// 0..25 -> 'a'..'z',26..51 -> 'A'..'Z'
int[] indexMap = new int[52];
for (int i = 0; i < 52; i++) {
indexMap[i] = i;
}
for (int opIdx = 0; opIdx < numOps; opIdx++) {
int op = scanner.nextInt();
char lch = scanner.next().charAt(0);
char rch = scanner.next().charAt(0);
if (op == 1) { // 小写区间转大写
int lo = lch - 'a';
int hi = rch - 'a';
for (int i = 0; i < 52; i++) {
int cur = indexMap[i];
if (cur < 26 && lo <= cur && cur <= hi) {
indexMap[i] = cur + 26;
}
}
} else { // 大写区间转小写
int lo = lch - 'A';
int hi = rch - 'A';
for (int i = 0; i < 52; i++) {
int cur = indexMap[i];
if (cur >= 26) {
int v = cur - 26;
if (lo <= v && v <= hi) {
indexMap[i] = v;
}
}
}
}
}
StringBuilder out = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
int t;
if (c >= 'a' && c <= 'z') {
t = indexMap[c - 'a'];
} else {
t = indexMap[26 + (c - 'A')];
}
if (t < 26) {
out.append((char)('a' + t));
} else {
out.append((char)('A' + (t - 26)));
}
}
System.out.println(out.toString());
}
}
Python:
import sys
tokens = iter(sys.stdin.read().split())
n = int(next(tokens))
num_ops = int(next(tokens))
text = next(tokens)
# 0..25 -> 'a'..'z',26..51 -> 'A'..'Z'
index_map = list(range(52))
for _ in range(num_ops):
op = int(next(tokens))
lch = next(tokens)
rch = next(tokens)
if op == 1: # 小写区间转大写
lo = ord(lch) - 97
hi = ord(rch) - 97
for i in range(52):
cur = index_map[i]
if cur < 26 and lo <= cur <= hi:
index_map[i] = cur + 26
else: # 大写区间转小写
lo = ord(lch) - 65
hi = ord(rch) - 65
for i in range(52):
cur = index_map[i]
if cur >= 26:
v = cur - 26
if lo <= v <= hi:
index_map[i] = v
out = []
for c in text:
if'a' <= c <= 'z':
t = index_map[ord(c) - 97]
else:
t = index_map[26 + ord(c) - 65]
out.append(chr(97 + t) if t < 26 else chr(65 + t - 26))
print(''.join(out))
第二题
给定一批训练样本与若干测试样本,需手写实现主成分分析(PCA): 仅保留第一主成分来压缩-重建数据,最后输出每个测试样本在重建后的均方误差(MSE)。步骤:
- 输入读取: train - 二维列表,每行是一个m维数值特征向量; test - 二维列表,维度同train。
- 去均值(mean-center):X_c = X - μ,其中μ是train的均值。
- 协方差矩阵(总体方差,ddof=0):Σ = (1/n) * X_c^T * X_c 。
- 求第一主成分: 用numpy.linalg.eigh得到全部特征对(λ_i, v_i); 按特征值从大到小选取第一主成分v_max; 方向标准化规则——若v_max首个非零分量为负,则整体乘以-1,保证方向唯一。
- 投影-重建: z = (x - μ)^T * v_max ,x_hat = μ + z * v_max 。
- 输出: 对每个测试样本计算MSE(x) = (1/m) * Σ(x_j - x_hat_j)^2 (结果保留两位小数,用JSON数组包裹所有测试样本的MSE输出)。
输入描述
标准输入仅一行,为如下JSON对象: { "train": [[...], [...], ...], "test": [[...], [...], ...] } 其中train长度n≥2,每行长度m≥2;test任意条数,维度同train;所有值为整数或浮点数,无额外空行。
输出描述
标准输出仅一行——测试集中每个样本MSE的字符串形式(两位小数),用JSON数组包裹。
补充说明:算法不含随机过程(无需设置随机种子)。仅允许使用NumPy库与Pandas库实现本题。使用总体方差np.var(x, ddof=0)。
样例输入
{"train": [[0,0],[0,1],[1,0],[1,1]], "test": [[0.5,0.5],[1.5,1.5]]}
样例输出
["0.00", "0.50"]
参考题解
首先读取输入的训练集和测试集数据并转换为 numpy 数组,计算训练集均值并对训练数据进行去均值处理;接着计算协方差矩阵,通过特征值分解得到特征值和特征向量,选取特征值最大的特征向量作为第一主成分并标准化其方向(若首个非零分量为负则取反);然后对每个测试样本,先去均值后投影到第一主成分,再从投影结果重建样本,计算原始样本与重建样本的均方误差并保留两位小数;最后将所有测试样本的 MSE 结果以 JSON 数组形式输出。
C++:
#include <iostream>
#include <vector>
#include <nlohmann/json.hpp>
#include <Eigen/Dense>
#include <iomanip>
#include <cmath>
using json = nlohmann::json;
using namespace Eigen;
using namespace std;
int main() {
// 读取输入JSON
json input_data;
cin >> input_data;
// 解析训练数据
auto& train_json = input_data["train"];
int num_samples = train_json.size();
int num_features = train_json[0].size();
MatrixXd train_data(num_samples, num_features);
for (int i = 0; i < num_samples; ++i) {
for (int j = 0; j < num_features; ++j) {
train_data(i, j) = train_json[i][j];
}
}
// 解析测试数据
auto& test_json = input_data["test"];
int num_test_samples = test_json.size();
MatrixXd test_data(num_test_samples, num_features);
for (int i = 0; i < num_test_samples; ++i) {
for (int j = 0; j < num_features; ++j) {
test_data(i, j) = test_json[i][j];
}
}
// 计算训练数据均值
RowVectorXd mean_train = train_data.colwise().mean();
// 中心化训练数据
MatrixXd centered_train = train_data.rowwise() - mean_train;
// 计算协方差矩阵
MatrixXd covariance_matrix = (centered_train.transpose() * centered_train) / num_samples;
// 计算特征值和特征向量
SelfAdjointEigenSolver<MatrixXd> solver(covariance_matrix);
VectorXd eigenvalues = solver.eigenvalues();
MatrixXd eigenvectors = solver.eigenvectors();
// 找到最大特征值对应的特征向量(第一主成分)
int max_eigen_idx = 0;
double max_eigenvalue = eigenvalues[0];
for (int i = 1; i < eigenvalues.size(); ++i) {
if (eigenvalues[i] > max_eigenvalue) {
max_eigenvalue = eigenvalues[i];
max_eigen_idx = i;
}
}
VectorXd first_component = eigenvectors.col(max_eigen_idx);
// 调整符号
int non_zero_idx = -1;
for (int i = 0; i < first_component.size(); ++i) {
if (abs(first_component[i]) > 1e-12) {
non_zero_idx = i;
break;
}
}
if (non_zero_idx != -1 && first_component[non_zero_idx] < 0) {
first_component = -first_component;
}
// 计算每个测试样本的MSE
vector<string> mse_results;
for (int i = 0; i < num_test_samples; ++i) {
RowVectorXd test_sample = test_data.row(i);
RowVectorXd centered_test = test_sample - mean_train;
// 计算投影
double projection = centered_test * first_component;
// 重建样本
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南
