【笔试刷题】阿里系列-2026.04.01-算法岗-改编真题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的
春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗
阿里系列-2026.04.01-算法岗
三题的考点比较分散。第一题按模 分组后重排;第二题直接找离给定数最近的质数;第三题先把可压缩操作落到同字符连续段里,再做一次背包。
题目一:小兰的跨步换位重排
这题最容易误判为只能做局部调整。实际上,位置差为 的交换会把整个数组拆成若干条链,同一条链上的元素可以任意重排。理解这一点后,对每条链单独降序排列,再按原位置写回,就能得到字典序最大的答案。
难度:中等
题目二:离观测值最近的质数
数据范围不大,不需要上筛法。按距离从小到大向两侧枚举,配合试除法判质数,就能同时处理最近距离和同距离取较小值这两个条件。
难度:简单
题目三:二进制日志的定长压缩
这题容易卡在压缩段不能重叠这个限制上。顺着这个约束往下分析,会发现每个压缩段都只能落在某个同字符连续段里。因此,先分别计算每一段能带来的长度减少量,再做一次总背包合并即可。
难度:中等
1. 小兰的跨步换位重排
问题描述
说明:阿里系列近期多条业务线笔试题基本共用同一套公开机试,淘天、阿里云等方向都可参考本场。
小兰 在整理一组分散存放的数据块。数组里的元素排在一条长线上,但调度器只允许她做一种固定步长的交换:如果两个位置正好相差 ,就可以直接对调它们。
现在给定一个长度为 的整数数组
和一个正整数
。你可以进行任意次如下操作:
- 选择一个下标
,满足
且
,将
与
交换。
请在可以无限次操作的前提下,求出最终能得到的字典序最大的数组。
数组字典序的比较方式如下:从第一个位置开始逐项比较,第一次出现不同的位置上,元素较大的那个数组字典序更大。
输入格式
每个测试文件包含多组测试数据。
- 第一行输入一个整数
,表示测试数据组数。
- 对于每组测试数据:
- 第一行输入两个整数
。
- 第二行输入
个整数
。
- 第一行输入两个整数
输出格式
对于每组测试数据,输出一行 个整数,表示在这些交换操作下能够得到的字典序最大的数组。
样例输入
3
7 2
1 9 3 8 2 7 4
5 3
10 1 5 9 2
6 6
4 3 2 1 0 -1
样例输出
4 9 3 8 2 7 1
10 2 5 9 1
4 3 2 1 0 -1
数据范围
- 所有测试数据中,
的总和不超过
| 样例 | 解释说明 |
|---|---|
| 样例1 第 1 组 | 位置会按模 |
| 样例1 第 2 组 | 位置按模 |
| 样例1 第 3 组 | 当 |
题解
先看哪些位置之间真的能互相到达
一次操作只能交换位置差恰好为 的两个元素。于是下标会形成若干条独立的链:
- ...
同一条链上的相邻点可以直接交换。因为相邻交换可以生成任意排列,所以同一条链上的元素最终可以任意重排。
而不同链之间永远不会连通,因此元素也不可能跨链移动。
为什么每条链都要按降序放回
字典序比较最关心前面的位置。
既然位置 只能从它所在的那条链里选元素,那么为了让整个数组字典序最大,位置
一定要拿到这条链里最大的元素;接下来同理,位置
要拿剩下元素里最大的;继续往后都一样。
所以每条链的最优策略就是:
- 取出这条链上的所有元素。
- 按从大到小排序。
- 再按链上的位置顺序依次放回。
把所有链都这样处理完,得到的就是全局字典序最大的数组。
复杂度分析
每个元素只会被分到一条链里一次,再参与一次排序。
- 时间复杂度:
。
- 空间复杂度:
。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def solve() -> None:
t = int(input())
out = []
for _ in range(t):
n, k = map(int, input().split())
a = list(map(int, input().split()))
ans = a[:]
for st in range(k):
pos = list(range(st, n, k))
vals = [a[i] for i in pos]
# 同一条链里的元素可以任意重排,按降序放回最优。
vals.sort(reverse=True)
for i, v in zip(pos, vals):
ans[i] = v
out.append(" ".join(map(str, ans)))
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<long long> a(n), ans(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
ans[i] = a[i];
}
for (int st = 0; st < k; ++st) {
vector<int> pos;
vector<long long> vals;
for (int i = st; i < n; i += k) {
pos.push_back(i);
vals.push_back(a[i]);
}
// 这条链上的元素可任意重排,降序放回即可让前面的位置尽量大。
sort(vals.begin(), vals.end(), greater<long long>());
for (int i = 0; i < (int) pos.size(); ++i) {
ans[pos[i]] = vals[i];
}
}
for (int i = 0; i < n; ++i) {
if (i) {
cout << ' ';
}
cout << ans[i];
}
cout << '\n';
}
return 0;
}
- Java
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
public class Main {
static class FastScanner {
private final BufferedInputStream in = new BufferedInputStream(System.in);
private final byte[] buf = new byte[1 << 16];
private int len = 0;
private int ptr = 0;
private int read() throws IOException {
if (ptr >= len) {
len = in.read(buf);
ptr = 0;
if (len <= 0) {
return -1;
}
}
return buf[ptr++];
}
int nextInt() throws IOException {
int c;
do {
c = read();
} while (c <= ' ' && c != -1);
int sign = 1;
if (c == '-') {
sign = -1;
c = read();
}
int val = 0;
while (c > ' ') {
val = val * 10 + c - '0';
c = read();
}
return val * sign;
}
long nextLong() throws IOException {
int c;
do {
c = read();
} while (c <= ' ' && c != -1);
long sign = 1;
if (c == '-') {
sign = -1;
c = read();
}
long val = 0;
while (c > ' ') {
val = val * 10 + c - '0';
c = read();
}
return val * sign;
}
}
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner();
StringBuilder out = new StringBuilder();
int T = fs.nextInt();
while (T-- > 0) {
int n = fs.nextInt();
int k = fs.nextInt();
long[] a = new long[n];
long[] ans = new long[n];
for (int i = 0; i < n; ++i) {
a[i] = fs.nextLong();
ans[i] = a[i];
}
for (int st = 0; st < k; ++st) {
ArrayList<Integer> pos = new ArrayList<>();
ArrayList<Long> vals = new ArrayList<>();
for (int i = st; i < n; i += k) {
pos.add(i);
vals.add(a[i]);
}
// 同链元素能任意排列,所以直接按降序放回。
vals.sort(Collections.reverseOrder());
for (int i = 0; i < pos.size(); ++i) {
ans[pos.get(i)] = vals.get(i);
}
}
for (int i = 0; i < n; ++i) {
if (i > 0) {
out.append(' ');
}
out.append(ans[i]);
}
out.append('\n');
}
System.out.print(out);
}
}
2. 离观测值最近的质数
问题描述
小柯 在调试一台会产生整数读数的采样器。某次读数记成了整数 ,她想把这个值校准到离它最近的质数上,作为后续实验的标准刻度。
也就是说,她需要找出一个质数 ,使得
尽可能小。
如果有两个质数到 的距离相同,那么输出较小的那个。
输入格式
每个测试文件包含多组测试数据。
- 第一行输入一个整数
,表示测试数据组数。
- 接下来
行,每行输入一个整数
。
输出格式
对于每组测试数据,输出一行一个整数,表示与 距离最近的质数。
样例输入
5
2
6
14
25
100
样例输出
2
5
13
23
101
数据范围
| 样例 | 解释说明 |
|---|---|
| 样例1 | 当 |
题解
直接按距离向两边找
如果当前距离是 ,那么离
距离正好为
的候选数只有两个:
因为题目要求:
- 先让距离最小。
- 若距离相同,取更小的那个。
所以最自然的做法就是从 开始往外扩:
- 先检查
是否是质数。
- 如果不是,再检查
是否是质数。
- 一旦找到答案,立刻输出。
这样第一次找到的质数,一定就是最近的;而且同距离时总是先检查较小的那个,也正好满足题目的次级要求。
质数判定怎么做
因为 ,并且测试数据最多只有
组,没有必要做复杂预处理。
对于一个数 :
- 若
,它不是质数。
- 若
能被
或
整除,就只有
和
本身是质数。
- 再往后只需要检查形如
的因子,直到
为止。
这个试除复杂度已经完全够用。
复杂度分析
设最终答案距离输入值的距离是 。
- 每次候选判质的复杂度是
。
- 总共大约会检查
个候选值。
在本题数据范围下,这个复杂度足够通过。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def is_prime(x: int) -> bool:
if x < 2:
return False
# 先单独处理最小的两个质因子。
if x % 2 == 0:
return x == 2
if x % 3 == 0:
return x == 3
# 之后只需要检查 6k±1 这两类候选因子。
d = 5
while d * d <= x:
if x % d == 0 or x % (d + 2) == 0:
return False
d += 6
return True
def solve() -> None:
t = int(input())
out = []
for _ in range(t):
x = int(input())
d = 0
while True:
y = x - d
# 同距离时要优先返回更小的那个,所以先检查 x-d。
if y >= 2 and is_prime(y):
out.append(str(y))
break
if d > 0:
z = x + d
# 只有较小端没命中时,才检查较大端。
if is_prime(z):
out.append(str(z))
break
d += 1
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
bool is_prime(int x) {
if (x < 2) {
return false;
}
// 先特判最小的两个质因子。
if (x % 2 == 0) {
return x == 2;
}
if (x % 3 == 0) {
return x == 3;
}
// 之后只检查 6k±1 形式的因子即可。
for (long long d = 5; d * d <= x; d += 6) {
if (x % d == 0 || x % (d + 2) == 0) {
return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int x;
cin >> x;
for (int d = 0;; ++d) {
int y = x - d;
// 同距离时优先取更小的答案,所以先查左边。
if (y >= 2 && is_prime(y)) {
cout << y << '\n';
break;
}
if (d > 0) {
int z = x + d;
// 左边没命中时,再检查右边。
if (is_prime(z)) {
cout << z << '\n';
break;
}
}
}
}
return 0;
}
- Java
import java.io.BufferedInputStream;
import java.io.IOException;
public class Main {
static class FastScanner {
private final BufferedInputStream in = new BufferedInputStream(System.in);
private final byte[] buf = new byte[1 << 16];
private int len =
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

查看11道真题和解析