【笔试刷题】蚂蚁-2026.03.15-算法岗-改编真题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的
春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗
蚂蚁-2026.03.15-算法岗
这一套前后层次很清楚:第 1 题是纯判断型热身,关键只是别被“大整数”三个字吓到;第 2 题虽然披着图学习外壳,但本质是按题面公式做一次聚合和小规模线性代数实现,属于整场最容易在细节上丢分的题;第 3 题回到经典字符串比较,真正难点是把“两端删字符”改写成固定长度窗口的最小字典序比较。
题目一:祝福乘积校验
这题本质是整除判定,不需要真的把两个超长整数乘出来。把字符串逐位对 取模后再相乘即可,做法非常直接。最容易错的地方是想当然上大整数库,结果把一个热身题写重了。
难度:简单
题目二:单层节点分类器
题面看着像机器学习,实际完全是实现题:无向图一跳均值聚合、岭回归闭式解、再接一次 sigmoid 判类。真正卡人的地方在于“自己也参与平均”“孤立点保持原向量”“浮点输出按题意保留到小数点后 6 位”这些工程细节。只要流程没有读错,这题并不需要任何额外模型知识。
难度:中等偏难
题目三:裁边后的最小片段
把左右删掉恰好 个字符后,剩下的一定是原串里的一个长度为
的连续子串,所以题目马上转成若干固定长度窗口的字典序最小值。难点不在贪心,而在怎么高效比较两个候选窗口;这里用双模哈希加二分 LCP 就够了。实现时最容易写炸的是比较函数边界和哈希下标。
难度:中等
1. 祝福乘积校验
问题描述
小基 在生日当天收到了很多朋友发来的祝福消息。第 位朋友会给出两个十进制大整数
。
小基 约定:如果 是
的倍数,就认为这份祝福通过了校验。
现在请对每位朋友分别判断,这份祝福是否通过校验。
输入格式
每个测试文件均包含多组测试数据。
第一行输入一个整数 ,表示朋友的数量。
接下来 行,每行输入两个由数字字符构成、且不含前导零的整数
。
输出格式
对于每位朋友,单独输出一行结果:
- 如果
是
的倍数,输出
Yes。 - 否则输出
No。
输出大小写必须与样例一致。
样例输入
5
2 3
6 11
22 9
25 44
121 132
样例输出
No
Yes
Yes
No
Yes
数据范围
- 单个测试文件内,所有
与
的位数之和不超过
题解
这题的核心不是“大整数乘法”,而是“整除判定”。
因为只需要判断 是否是
的倍数,所以根本没有必要真的把两个超长整数乘出来。只要保留模
的结果就够了。
设:
那么:
于是只要判断:
就能得到答案。
怎么求超长整数对
取模
把数字当成字符串逐位读入。
如果当前已经处理出的前缀模值是 cur,读到新的一位数字 d 之后,新模值就是:
整段字符串扫完,就得到了这个大整数对 的模。
复杂度分析
设某组数据中两个字符串长度之和为 。
逐位取模只需要线性扫描,因此这一组的复杂度是 。
整份测试文件中,所有数字总位数不超过 ,所以总复杂度就是
,额外空间复杂度为
。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def mod66(s: str) -> int:
val = 0
for ch in s:
val = (val * 10 + ord(ch) - 48) % 66
return val
def solve() -> None:
t = int(input())
out = []
for _ in range(t):
x, y = input().split()
ok = mod66(x) * mod66(y) % 66 == 0
out.append("Yes" if ok else "No")
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
int mod66(const string& s) {
int val = 0;
for (char ch : s) {
val = (val * 10 + (ch - '0')) % 66;
}
return val;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
string x, y;
cin >> x >> y;
bool ok = 1LL * mod66(x) * mod66(y) % 66 == 0;
cout << (ok ? "Yes" : "No") << '\n';
}
return 0;
}
- Java
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringJoiner;
public class Main {
static int mod66(String s) {
int val = 0;
for (int i = 0; i < s.length(); i++) {
val = (val * 10 + s.charAt(i) - '0') % 66;
}
return val;
}
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner(System.in);
int t = fs.nextInt();
StringBuilder sb = new StringBuilder();
for (int cs = 0; cs < t; cs++) {
String x = fs.next();
String y = fs.next();
boolean ok = (long) mod66(x) * mod66(y) % 66 == 0;
sb.append(ok ? "Yes" : "No").append('\n');
}
System.out.print(sb);
}
static class FastScanner {
private final InputStream in;
private final byte[] buf = new byte[1 << 16];
private int ptr = 0;
private int len = 0;
FastScanner(InputStream is) {
in = new BufferedInputStream(is);
}
private int read() throws IOException {
if (ptr >= len) {
len = in.read(buf);
ptr = 0;
if (len <= 0) {
return -1;
}
}
return buf[ptr++];
}
String next() throws IOException {
int c;
do {
c = read();
} while (c <= ' ' && c != -1);
StringBuilder sb = new StringBuilder();
while (c > ' ') {
sb.append((char) c);
c = read();
}
return sb.toString();
}
int nextInt() throws IOException {
return Integer.parseInt(next());
}
}
}
2. 单层节点分类器
问题描述
小基 正在整理一张无向关系网络。图中共有 个节点,节点编号为
,每个节点都带有一个长度为
的原始特征向量。
现在需要按照下面这套固定流程,实现一个单层 GraphSAGE-Mean 节点分类器:
- 图是无向图,没有自环,输入给出邻接边
edges。 - 原始特征矩阵记为
。
- 先做一跳平均聚合,得到新特征矩阵
。对于节点
:
如果节点 的度数为
,那么直接有
。
- 只在训练节点上已知标签
。设训练节点对应的特征矩阵是
,标签向量是
,并固定
,训练目标为:
其闭式解为:
- 对任意节点
,预测概率为:
其中 。阈值取
,若
则预测标签为
,否则为
。
请输出权重向量、测试点的预测概率,以及测试点的预测标签。
输入格式
输入只有一行,是一个 JSON 对象:
{
"nodes": [[...], [...], ...],
"edges": [[u1, v1], [u2, v2], ...],
"train_idx": [i1, i2, ...],
"train_y": [0, 1, ...],
"test_idx": [j1, j2, ...]
}
其中:
nodes表示原始特征矩阵;edges表示无向边;train_idx表示训练节点下标;train_y表示训练节点标签;test_idx表示需要预测的节点下标。
输出格式
输出一行 JSON:
{
"weights": [...],
"test_proba": [...],
"test_pred": [...]
}
要求:
weights长度为;
test_proba长度等于test_idx的长度;- 所有浮点数都按照
round(x, 6)的规则保留到小数点后位。
样例输入
{"nodes":[[0,0],[0.2,0.1],[0.1,-0.1],[4,4],[4.2,3.9]],"edges":[[0,1],[0,2],[3,4]],"train_idx":[0,3],"train_y":[0,1],"test_idx":[1,4]}
样例输出
{"weights":[0.085354,0.164463],"test_proba":[0.50419,0.730977],"test_pred":[1,1]}
数据范围
题解
这题本质上是一道按固定公式实现的题,关键是不要把流程理解错。
第一步:做一跳均值聚合
先按无向图建邻接表。
然后对每个节点 ,把自己和所有一跳邻居的特征向量加起来,再除以
,就得到新的特征向量
。
这里有两个容易错的点:
- 这一步是自己也要参与平均,不是只平均邻居。
- 图是无向图,所以每条边要双向加入。
第二步:只在训练节点上做岭回归
题目已经把目标函数和闭式解都写出来了,所以不需要再去训练什么神经网络,也不需要自己迭代梯度下降。
把训练节点对应的那些行抽出来,记成矩阵 ,再把标签写成列向量
。
接着计算:
问题就变成了解线性方程:
由于 ,直接用高斯消元就够了。
第三步:算概率并判类
拿到 以后,对测试节点
计算:
如果 ,预测标签就是
,否则是
。
为什么这样做是对的
因为题目已经明确规定了:
- 特征如何聚合;
- 训练目标函数是什么;
- 闭式解怎么写;
- 预测时如何从线性结果映射到概率。
所以只要严格按这套流程实现,得到的结果就是题目要求的标准答案。
复杂度分析
设节点数为 ,特征维度为
,边数为
。
- 建图和聚合的复杂度是
。
- 计算
与
的复杂度是
。
- 解一个
的线性方程组,复杂度是
。
由于本题 都很小,所以整体代价非常低。
参考代码
- Python
import json
import math
import sys
input = lambda: sys.stdin.readline().strip()
LAMBDA = 0.01
def sigmoid(x: float) -> float:
if x >= 0:
e = math.exp(-x)
return 1.0 / (1.0 + e)
e = math.exp(x)
return e / (1.0 + e)
def gauss(a, b):
n = len(a)
for col in range(n):
pivot = col
for row in range(col, n):
if abs(a[row][col]) > abs(a[pivot][col]):
pivot = row
a[col], a[pivot] = a[pivot], a[col]
b[col], b[pivot] = b[pivot], b[col]
base = a[col][col]
for j in range(col, n):
a[col][j] /= base
b[col] /= base
for row in range(n):
if row == col:
continue
fac = a[row][col]
if abs(fac) < 1e-12:
continue
for j in range(col, n):
a[row][j] -= fac * a[col][j]
b[row] -= fac * b[col]
return b
def solve() -> None:
data = json.loads(input())
nodes = data["nodes"]
edges = data["edges"]
train_idx = data["train_idx"]
train_y = data["train_y"]
test_idx = data["test_idx"]
n = len(nodes)
d = len(nodes[0])
g = [[] for _ in range(n)]
for u, v in edges:
g[u].append(v)
g[v].append(u)
h = [[0.0] * d for _ in range(n)]
for i in range(n):
cnt = len(g[i]) + 1
for k in range(d):
cur = nodes[i][k]
for v in g[i]:
cur += nodes[v][k]
h[i][k] = cur / cnt
a = [[0.0] * d for _ in range(d)]
b = [0.0] * d
for idx, y in zip(train_idx, train_y):
row = h[idx]
for i in range(d):
b[i] += row[i] * y
for j in range(d):
a[i][j] += row[i] * row[j]
for i in range(d):
a[i][i] += LAMBDA
w = gauss(a, b)
proba = []
pred = []
for idx in test_idx:
z = 0.0
for i in range(d):
z += h[idx][i] * w[i]
p = sigmoid(z)
proba.append(round(p, 6))
pred.append(1 if p >= 0.5 else 0)
ans = {
"weights": [round(x, 6) for x in w],
"test_proba": proba,
"test_pred": pred,
}
sys.stdout.write(json.dumps(ans, ensure_ascii=False, separators=(",", ":")))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
const double LAMBDA = 0.01;
struct Parser {
string s;
int p = 0;
explicit Parser(string str) : s(std::move(str)) {}
void skip() {
while (p < (int)s.size() && isspace((unsigned char)s[p])) {
++p;
}
}
void expect(char c) {
skip();
if (p >= (int)s.size() || s[p] != c) {
throw runtime_error("parse error");
}
++p;
}
bool eat(char c) {
skip();
if (p < (int)s.size() && s[p] == c) {
++p;
return true;
}
return false;
}
string parseString() {
skip();
expect('"');
string res;
while (p < (int)s.size() && s[p] != '"') {
res.push_back(s[p++]);
}
expect('"');
return res;
}
double parseNumber() {
skip();
int st = p;
if (s[p] == '-') {
++p;
}
while (p < (int)s.size() && isdigit((unsigned char)s[p])) {
++p;
}
if (p < (int)s.size() && s[p] == '.') {
++p;
while (p < (int)s.size() && isdigit((unsigned char)s[p])) {
++p;
}
}
return stod(s.substr(st, p - st));
}
vector<double> parseDoubleArray() {
vector<double> res;
expect('[');
if (eat(']')) {
return res;
}
while (true) {
res.push_back(parseNumber());
if (eat(']')) {
break;
}
expect(',');
}
return res;
}
vector<int> parseIntArray() {
vector<int> res;
expect('[');
if (eat(']')) {
return res;
}
while (true) {
res.push_back((int)llround(parseNumber()));
if (eat(']')) {
break;
}
expect(',');
}
return res;
}
vector<vector<double>> parseDoubleMatrix() {
vector<vector<double>> res;
expect('[');
if (eat(']')) {
return res;
}
while (true) {
res.push_back(parseDoubleArray());
if (eat(']')) {
break;
}
expect(',');
}
return res;
}
vector<vector<int>> parseIntMatrix() {
vector<vector<int>> res;
expect('[');
if (eat(']')) {
return res;
}
while (true) {
res.push_back(parseIntArray());
if (eat(']')) {
break;
}
expect(',');
}
return res;
}
};
double sigmoid(double x) {
if (x >= 0) {
double e = exp(-x);
return 1.0 / (1.0 + e);
}
double e = exp(x);
return e / (1.0 + e);
}
vector<double> gauss(vector<vector<double>> a, vector<double> b) {
int n = (int)a.size();
for (int col = 0; col < n; ++col) {
int pivot = col;
for (int row = col; row < n; ++row) {
if (fabs(a[row][col]) > fabs(a[pivot][col])) {
pivot = row;
}
}
swap(a[col], a[pivot]);
swap(b[col], b[pivot]);
double base = a[col][col];
for (int j = col; j < n; ++j) {
a[col][j] /= base;
}
b[col] /= base;
for (int row = 0; row < n; ++row) {
if (row == col) {
continue;
}
double fac = a[row][col];
if (fabs(fac) < 1e-12) {
continue;
}
for (int j = col; j < n; ++j) {
a[row][j] -= fac * a[col][j];
}
b[row] -= fac * b[col];
}
}
return b;
}
string fmt(double x) {
double y = round(x * 1e6) / 1e6;
if (fabs(y) < 5e-13) {
y = 0.0;
}
ostringstream oss;
oss << fixed << setprecision(6) << y;
string s = oss.str();
while (!s.empty() && s.back() == '0') {
s.pop_back();
}
if (!s.empty() && s.back() == '.') {
s += '0';
}
if (s.empty() || s == "-0") {
s = "0.0";
}
if (s.find('.') == string::npos) {
s += ".0";
}
return s;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string line, tmp;
while (getline(cin, tmp)) {
line += tmp;
}
Parser ps(line);
vector<vector<double>> nodes;
vector<vector<int>> edges;
vector<int> trainIdx, trainY, testIdx;
ps.expect('{');
while (true) {
string key = ps.parseString();
ps.expect(':');
if (key == "nodes") {
nodes = ps.parseDoubleMatrix();
} else if (key == "edges") {
edges = ps.parseIntMatrix();
} else if (key == "train_idx") {
trainIdx = ps.parseIntArray();
} else if (key ==
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力