【春招笔试】2025.03.30-阿里云研发岗改编题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题(建议用时:90分钟/套)
- 对照解析查漏补缺
- 配套练习题库
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
春秋招合集 -> 互联网必备刷题宝典🔗
题目一:音乐旋律延音器
1️⃣:解析输入的旋律字符串,按照乐句进行处理
2️⃣:对每个乐句添加延音效果,在乐句前加上p个下划线,并截取与原乐句等长的部分
3️⃣:按照原乐句的分隔方式输出延音后的结果
难度:简单
这道题目要求实现音乐中的延音效果,核心思路是将原旋律向后移动p个位置,前面用下划线填充。通过逐行读取输入并按乐句处理,我们可以高效地生成延音效果,时间复杂度为O(n)。
题目二:二进制优化大师
1️⃣:分析二进制末尾0的数量与因子2的关系
2️⃣:计算每种操作(+1一次或两次)对因子2数量的影响
3️⃣:选择最优策略,使最终乘积中因子2的数量最大化
难度:中等
这道题目考察二进制特性的理解和贪心策略的应用。关键是认识到二进制表示末尾的连续0与因子2的关系,并计算不同操作对增加因子2的贡献。通过比较两种操作策略(对两个元素各操作一次,或对同一元素操作两次),我们可以在O(n)时间内求得最优解。
题目三:文学回文工程
1️⃣:理解回文特性,分析字母配置策略
2️⃣:计算可用于构建回文的最大字符数量
3️⃣:计算树的直径,与可用回文字符数量取较小值
难度:中等偏难
这道题目结合了回文性质和树结构的分析。通过观察回文串的特性(字符出现次数的奇偶性)以及树中路径的特点,我们可以得出一个优雅的解法:树的文学价值不会超过树的直径和可用回文字符数量中的较小值。算法实现上,计算树的直径使用两次BFS,整体时间复杂度为O(n)。
01. 音乐旋律延音器
问题描述
小柯是一位音乐爱好者,最近她正在研究旋律和它的延音效果。在音乐表示法中,小柯使用小写字母和连接线(-)表示音符和持续时间,并用竖线'|'来划分乐句(小节)。
例如,|do-do-re|re---|表示两个乐句,第一个乐句长度为8,内容为"do-do-re";第二个乐句长度为5,内容为"re---"。
小柯想要为旋律添加延音效果。延音效果的规则如下:
- 延音效果的乐句数量和各个乐句的长度与原旋律完全一致
- 延音效果会比原旋律晚p个长度出现,延音未出现时用下划线替代
- 乐句结束时未输出完整的延音会被直接截断
具体地,为实现延音效果,需要在每个乐句前面加上p条下划线,然后截取与原乐句长度相同的部分,得到该乐句的延音效果。
输入格式
第一行输入两个整数 (
;
)代表原旋律字符串总长度(包括'|'在内)和延音延迟的长度。
此后若干行,一共输入 个字符,代表原旋律字符串。保证每行的首尾均为竖线(|),每个乐句的长度至少为1,乐句中的字符仅为小写字母和连接线(-)。
输出格式
根据输入,输出若干行,代表添加延音效果后的旋律字符串。
样例输入
16 2
|do-do-re|re---|
样例输出
|__do-do-|__re-|
数据范围
样例1 | 第一个乐句:do-do-re 变为__do-do- 第二个乐句:re--- 变为__re- |
题解
这道题目其实是在模拟音乐中的延音效果。简单来说,就是把原本的旋律向后移动p个位置,前面空出来的位置用下划线填充,而且每个乐句的长度要保持不变。
思路很直观:
- 逐行读取输入字符串
- 对于每行字符串,我们需要按乐句(也就是由'|'分隔的部分)进行处理
- 对于每个乐句,我们执行以下操作:
- 在乐句前加上p个下划线
- 截取与原乐句等长的部分作为新乐句内容
- 将新乐句内容添加到结果中
关键点在于理解乐句的处理方式。比如原乐句是"do-do-re",延迟长度p=2,那么:
- 先在前面加上2个下划线,变成"__do-do-re"
- 然后截取长度为8的部分(原乐句长度),得到"__do-do-"
这样处理每个乐句,最后输出的就是延音效果后的旋律。
注意事项:
- 需要小心处理每行字符串的解析,确保正确识别出每个乐句
- 当p=0时,延音效果就是原旋律本身
- 当p很大时,可能整个乐句都会变成下划线
时间复杂度是O(n),其中n是输入字符串的总长度。这个算法非常高效,因为我们只需要遍历一次输入字符串即可。
参考代码
- Python
import sys
input = lambda:sys.stdin.readline().strip()
# 读取输入
n, p = map(int, input().split())
lines = []
chars_read = 0
# 读取所有行,直到达到n个字符
while chars_read < n:
line = input()
lines.append(line)
chars_read += len(line)
# 处理每一行
for line in lines:
if not line:
print()
continue
res = ""
i = 0
# 处理每个乐句
while i < len(line):
if line[i] == '|':
res += '|'
i += 1
start = i
# 找到乐句的结束位置
while i < len(line) and line[i] != '|':
i += 1
if i > start: # 确保有乐句内容
seg = line[start:i]
# 创建延音效果:p个下划线加上原乐句,然后截取
delay = '_' * p + seg
trans = delay[:len(seg)]
res += trans
else:
# 理论上不会执行到这里
res += line[i]
i += 1
print(res)
- Cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long p;
cin >> n >> p;
cin.ignore(); // 忽略换行符
string line;
// 逐行处理输入
while (getline(cin, line)) {
if (line.empty()) {
cout << endl;
continue;
}
string rslt;
int idx = 0;
// 处理每个乐句
while (idx < line.size()) {
if (line[idx] == '|') {
rslt += '|';
idx++;
int bgn = idx;
// 找到乐句结束位置
while (idx < line.size() && line[idx] != '|') {
idx++;
}
// 有乐句内容时进行处理
if (idx > bgn) {
string ph = line.substr(bgn, idx - bgn);
// 创建延音效果
string tmp = string(p, '_') + ph;
string nph = tmp.substr(0, ph.size());
rslt += nph;
}
} else {
// 理论上不会执行到这里
rslt += line[idx];
idx++;
}
}
cout << rslt << endl;
}
return 0;
}
- Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] inp = br.readLine().split(" ");
int n = Integer.parseInt(inp[0]);
long p = Long.parseLong(inp[1]);
StringBuilder rslt = new StringBuilder();
String line;
int read = 0;
// 逐行处理输入
while ((line = br.readLine()) != null && read < n) {
read += line.length();
if (line.isEmpty()) {
rslt.append("\n");
continue;
}
StringBuilder curLine = new StringBuilder();
int idx = 0;
// 处理每个乐句
while (idx < line.length()) {
if (line.charAt(idx) == '|') {
curLine.append('|');
idx++;
int bgn = idx;
// 找到乐句结束位置
while (idx < line.length() && line.charAt(idx) != '|') {
idx++;
}
// 处理乐句
if (idx > bgn) {
String ph = line.substring(bgn, idx);
// 创建延音效果
StringBuilder delay = new StringBuilder();
for (int i = 0; i < p; i++) {
delay.append('_');
}
delay.append(ph);
String nph = delay.substring(0, ph.length());
curLine.append(nph);
}
} else {
// 理论上不会执行到这里
curLine.append(line.charAt(idx));
idx++;
}
}
rslt.append(curLine).append("\n");
}
System.out.print(rslt);
}
}
02. 二进制优化大师
问题描述
小基是一位精通二进制的工程师,最近她面临了一个有趣的挑战。
小基手上有一个数字序列,她可以对序列中的元素进行最多两次操作:每次操作可以选择一个元素并使其增加1。小基希望通过这些操作,使得序列中所有元素的乘积在二进制表示中末尾有尽可能多的连续0。
众所周知,二进制表示末尾有连续的0,意味着这个数包含了更多的因子2。小基需要合理分配她的两次操作,使得最终乘积末尾的0尽可能多。
输入格式
第一行输入一个整数 (
),表示序列的长度。
第二行输入 个整数
(
),表示序列中的各个元素。
输出格式
输出一个整数,表示经过最多两次操作后,序列所有元素乘积的二进制表示末尾最多可能有多少个连续的0。
样例输入
2
1 2
样例输出
6
数据范围
样例1 | 对序列[1,2],可以对第一个元素进行两次+1操作,使序列变为[3,2]。 乘积为6,二进制表示为110,末尾有1个0。 或者可以对第一个元素操作一次,对第二个元素操作一次,使序列变为[2,3]。 乘积为6,二进制表示为110,末尾有1个0。 还可以对第一个元素进行一次操作,使序列变为[2,2]。 乘积为4,二进制表示为100,末尾有2个0。 最优方案是将序列变为[2,2],此时末尾有2个0。 |
题解
这道题目的核心在于理解二进制表示末尾连续0的数量与数字中因子2的关系。
首先,我们知道一个数的二进制表示末尾有k个连续0,就意味着这个数可以被2^k整除,也就是说这个数包含至少k个因子2。
对于一个数组的所有元素乘积,它的二进制末尾0的个数等于所有元素中因子2的总数。
那么这个问题就转化为:如何通过最多两次+1操作,最大化数组元素中因子2的总数?
我们有三种可能的操作策略:
- 对两个不同的元素各执行一次+1操作
- 对同一个元素连续执行两次+1操作
- 只执行一次+1操作,或者不执行任何操作
对于每个元素a_i,我们需要计算:
- 执行一次+1操作后,因子2的增量:f(a_i+1) - f(a_i)
- 执行两次+1操作后,因子2的增量:f(a_i+2) - f(a_i)
其中f(x)表示x中因
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力