【春招笔试】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---"。

小柯想要为旋律添加延音效果。延音效果的规则如下:

  1. 延音效果的乐句数量和各个乐句的长度与原旋律完全一致
  2. 延音效果会比原旋律晚p个长度出现,延音未出现时用下划线替代
  3. 乐句结束时未输出完整的延音会被直接截断

具体地,为实现延音效果,需要在每个乐句前面加上p条下划线,然后截取与原乐句长度相同的部分,得到该乐句的延音效果。

输入格式

第一行输入两个整数 )代表原旋律字符串总长度(包括'|'在内)和延音延迟的长度。

此后若干行,一共输入 个字符,代表原旋律字符串。保证每行的首尾均为竖线(|),每个乐句的长度至少为1,乐句中的字符仅为小写字母和连接线(-)。

输出格式

根据输入,输出若干行,代表添加延音效果后的旋律字符串。

样例输入

16 2
|do-do-re|re---|

样例输出

|__do-do-|__re-|

数据范围

样例 解释说明
样例1 第一个乐句:do-do-re 变为__do-do-
第二个乐句:re--- 变为__re-

题解

这道题目其实是在模拟音乐中的延音效果。简单来说,就是把原本的旋律向后移动p个位置,前面空出来的位置用下划线填充,而且每个乐句的长度要保持不变。

思路很直观:

  1. 逐行读取输入字符串
  2. 对于每行字符串,我们需要按乐句(也就是由'|'分隔的部分)进行处理
  3. 对于每个乐句,我们执行以下操作:
    • 在乐句前加上p个下划线
    • 截取与原乐句等长的部分作为新乐句内容
    • 将新乐句内容添加到结果中

关键点在于理解乐句的处理方式。比如原乐句是"do-do-re",延迟长度p=2,那么:

  1. 先在前面加上2个下划线,变成"__do-do-re"
  2. 然后截取长度为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操作
  2. 对同一个元素连续执行两次+1操作
  3. 只执行一次+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%内容,订阅专栏后可继续查看/也可单篇购买

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

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

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务