阿里实习笔试 阿里春招实习 阿里笔试真题解析 0401

笔试时间:2026年4月1日

往年笔试合集:

2023春招秋招笔试合集

2024春招秋招笔试合集

第一题:等差数列模最大值

题目

给定一个无穷项等差数列 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 + splitStreamTokenizer 时确认精度,下面给出稳妥版本:

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()

第二题:括号序列平衡

题目

我们称一个括号序列为"平衡的括号序列",当且仅当满足以下归纳定义:

  1. 空串是平衡的;
  2. 若字符串 A 是平衡的,则"(A)"是平衡的;
  3. 若字符串 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 春招笔试合集 文章被收录于专栏

2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南

全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

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