阿里实习笔试 阿里春招实习 阿里笔试真题解析 0401
笔试时间:2026年4月1日
往年笔试合集:
第一题:等差数列模最大值
题目
给定一个无穷项等差数列 ai = a0 + i·d(i ≥ 0),以及一个正整数 m。定义余数为 ai mod m(取值范围为 [0, m))。请你计算在所有 i ≥ 0 中,ai mod m 的最大可能值,并输出该最大值。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1 ≤ T ≤ 10^5)表示数据组数。
每组测试数据描述如下:一行输入三个整数 a0, d, m(0 ≤ a0, d ≤ 10^18, 1 ≤ m ≤ 10^18)。
输出描述
对于每组测试数据,输出一行一个整数,表示能得到的最大值。
样例输入
3 5 4 7 3 10 6 100 100 100
样例输出
6 5 0
参考题解
解题思路:
考点为裴蜀定理与最大公约数(GCD)。
根据数论中的裴蜀定理,等差数列 a0 + i·d 模 m 的结果等价于在 a0 % gcd(d, m) 的基础上,加上 gcd(d, m) 的若干整数倍。为了求 [0, m-1] 范围内的最大可能值,直接用 m 减去它们的最大公约数,再加上初始余数的偏移量即可。
核心公式:m - gcd + (a0 % gcd),单次查询时间复杂度 O(1)。
C++:
#include <iostream>
#include <numeric>
using namespace std;
typedef long long ll;
void solve() {
ll a, d, m;
cin >> a >> d >> m;
ll g = std::gcd(d, m);
// 直接计算并输出结果
cout << m - g + (a % g) << "\n";
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int t;
if (cin >> t) {
while (t--) {
solve();
}
}
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));
StreamTokenizer in = new StreamTokenizer(br);
StringBuilder sb = new StringBuilder();
in.nextToken();
int t = (int) in.nval;
while (t-- > 0) {
in.nextToken(); long a = (long) in.nval;
in.nextToken(); long d = (long) in.nval;
in.nextToken(); long m = (long) in.nval;
long g = gcd(d, m);
// 直接计算并输出结果
sb.append(m - g + (a % g)).append('\n');
}
System.out.print(sb);
}
static long gcd(long a, long b) {
while (b != 0) {
long t = a % b;
a = b;
b = t;
}
return a;
}
}
注:由于 a, d, m 可达 10^18,若用
StreamTokenizer精度不足,建议使用BufferedReader+split或StreamTokenizer时确认精度,下面给出稳妥版本:
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));
StringBuilder sb = new StringBuilder();
int t = Integer.parseInt(br.readLine().trim());
while (t-- > 0) {
StringTokenizer st = new StringTokenizer(br.readLine());
long a = Long.parseLong(st.nextToken());
long d = Long.parseLong(st.nextToken());
long m = Long.parseLong(st.nextToken());
long g = gcd(d, m);
sb.append(m - g + (a % g)).append('\n');
}
System.out.print(sb);
}
static long gcd(long a, long b) {
while (b != 0) {
long t = a % b;
a = b;
b = t;
}
return a;
}
}
Python:
import sys
from math import gcd
def solve():
a, d, m = map(int, sys.stdin.readline().split())
g = gcd(d, m)
# 直接计算并输出结果
sys.stdout.write(f"{m - g + (a % g)}\n")
def main():
t = int(sys.stdin.readline())
for _ in range(t):
solve()
if __name__ == "__main__":
main()
第二题:括号序列平衡
题目
我们称一个括号序列为"平衡的括号序列",当且仅当满足以下归纳定义:
- 空串是平衡的;
- 若字符串 A 是平衡的,则"(A)"是平衡的;
- 若字符串 A 与 B 均是平衡的,则"AB"(连接)是平衡的。
例如:括号序列 () 与 (()) 是平衡的;而 )、)()、( 不是。
给定一个只由 ( 与 ) 组成、长度为 n 的字符串 s(n 为偶数)。你可以进行若干次如下操作:
选择一个下标 i(1 ≤ i ≤ n),将字符 s_i 的"类型"镜像翻转:若 s_i = ( 则变为 );若 s_i = ) 则变为 (。其余位置保持不变。
请你计算使 s 变为平衡括号序列所需的最少操作次数,并输出任意一组达到最少次数的操作下标序列。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1 ≤ T ≤ 10^5)表示数据组数。
每组测试数据描述如下:
- 第一行输入一个偶数 n(2 ≤ n ≤ 2 × 10^5);
- 第二行输入一个长度为 n 的字符串 s(仅包含字符
(与))。
保证所有测试中 n 的总和不超过 2 × 10^5。
输出描述
对于每组测试数据,输出两行:
- 第一行输出一个整数 k,表示最少操作次数;
- 第二行输出 k 个整数 p1, p2, ..., pk(1 ≤ pj ≤ n),表示依次在位置 pj 处进行单点镜像翻转。若 k = 0,第二行留空即可。
若存在多组最优方案,输出任意一组。
样例输入
3 2 )( 4 ()() 4 ))((
样例输出
2 1 2 0 2 1 4
样例说明
- 样例一: 依次翻转位置 1, 2,
)(→(),最少 2 次。 - 样例二: s 已是平衡串,k = 0,第二行留空。
- 样例三: 翻转位置 1 与 4,
))((→()(),最少 2 次。
参考题解
解题思路:
采用"栈模拟 + 贪心"的策略。
首先利用栈剔除掉原串中已经合法匹配的括号对。剔除完毕后,残留的无效序列必定呈现 ))))(((( 的极端形态(左半段全为右括号,右半段全为左括号)。
为保证用最少操作次数达到整体平衡,我们只需贪心地将左半侧前一半的 ) 进行翻转,并将右半侧后一半的 ( 进行翻转。将这些位置的索引提取出来即可。
C++:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void solve() {
int len;
string str;
cin >> len >> str;
vector<int> stk, R_fail;
// 线性遍历,抵消合法括号
for (int i = 0; i < len; ++i) {
if (str[i] == '(') {
stk.push_back(i + 1);
} else {
if (!stk.empty()) {
stk.pop_back();
} else {
R_fail.push_back(i + 1);
}
}
}
vector<int> ans;
// 处理剩余的右括号(取前一半向上取整)
int r_sz = R_fail.size();
for (int i = 0; i < (r_sz + 1) / 2; ++i) {
ans.push_back(R_fail[i]);
}
// 处理剩余的左括号(取后一半向上取整)
int l_sz = stk.size();
int l_half = (l_sz + 1) / 2;
for (int i = l_sz - l_half; i < l_sz; ++i) {
ans.push_back(stk[i]);
}
// 格式化输出
cout << ans.size() << "\n";
for (int i = 0; i < (int)ans.size(); ++i) {
cout << ans[i] << (i == (int)ans.size() - 1 ? "" : " ");
}
cout << "\n";
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int tc;
if (cin >> tc) {
for (int i = 0; i < tc; ++i) {
solve();
}
}
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));
StringBuilder sb = new StringBuilder();
int tc = Integer.parseInt(br.readLine().trim());
for (int t = 0; t < tc; t++) {
int len = Integer.parseInt(br.readLine().trim());
String str = br.readLine();
List<Integer> stk = new ArrayList<>();
List<Integer> rFail = new ArrayList<>();
// 线性遍历,抵消合法括号
for (int i = 0; i < len; i++) {
if (str.charAt(i) == '(') {
stk.add(i + 1);
} else {
if (!stk.isEmpty()) {
stk.remove(stk.size() - 1);
} else {
rFail.add(i + 1);
}
}
}
List<Integer> ans = new ArrayList<>();
// 处理剩余的右括号(取前一半向上取整)
int rSz = rFail.size();
for (int i = 0; i < (rSz + 1) / 2; i++) {
ans.add(rFail.get(i));
}
// 处理剩余的左括号(取后一半向上取整)
int lSz = stk.size();
int lHalf = (lSz + 1) / 2;
for (int i = lSz - lHalf; i < lSz; i++) {
ans.add(stk.get(i));
}
// 格式化输出
sb.appen
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南
查看12道真题和解析