【笔试刷题】小红书-2026.03.25-第二套-改编真题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的
春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗
小红书-2026.03.25-第二套
题目一:小毛的用户数据整理
把每条记录的三个字段按类型识别出来即可:带小数点的是经验值,全小写字符串是用户名,剩下的整数就是用户编号。存下来按编号排序输出。
难度:Easy
题目二:小兰的内容管理
先把字符串压成连续段,再按轮模拟“除第一段外每段都删掉一个字符”。关键不在暴力删字符串,而在于说明为什么连续段重建后的总工作量仍然只有线性级别。
难度:Mid
题目三:图书馆座位重排系统
本质是在一次合法操作内,尽可能把更小的字符提前。需要抓住三元环的结构,再从左到右找第一处能改善字典序的位置。
难度:High
1. 小毛的用户数据整理
问题描述
小毛最近从一份用户数据库里导出了一批记录。每条记录本来都包含三个字段:
- 用户编号:一个正整数,且所有记录中的编号互不相同;
- 用户名称:一个只由小写字母组成的非空字符串;
- 用户经验:一个恰好保留两位小数的非负数。
不过导出工具出了点问题,同一条记录里的这三个字段顺序被打乱了,只能保证它们之间仍然用空格分隔。
现在给出 条这样的记录,请你先恢复每条记录的三个字段,再按照用户编号从小到大排序,最后按
用户编号 用户名称 用户经验 的顺序输出。
输入格式
第一行输入一个整数 (
),表示记录条数。
接下来 行,每行输入三个用空格分隔的字段,分别是某条记录被打乱后的三个部分。保证:
- 用户编号是一个整数,满足
;
- 用户名称只由小写英文字母组成,长度在
到
之间;
- 用户经验是一个恰好保留两位小数的非负数,满足
。
输出格式
输出 行。按用户编号从小到大排序后,每行输出一条记录,格式为:
用户编号 用户名称 用户经验
样例输入
3
xhs 12 106.70
0.00 abc 11
6 xhs 666.66
样例输出
6 xhs 666.66
11 abc 0.00
12 xhs 106.70
数据范围
- 用户编号互不相同
- 用户名称只包含小写字母
- 用户经验恰好保留两位小数
| 样例 | 解释说明 |
|---|---|
| 样例1 | 三条记录恢复后分别是 |
题解
这一题没有什么复杂算法,关键在于正确识别每个字段的类型。
一条记录里只有三种字段:
- 全小写字母串,一定是用户名;
- 含有小数点的字段,一定是经验值;
- 剩下那个整数,就是用户编号。
所以我们可以对每一行的三个字符串逐个分类,恢复成 (id, name, exp) 这样的结构。
需要注意的一点是:经验值输出时要保留两位小数。最稳的做法是直接把经验值按字符串保存,排序时只按 id 比较,最后原样输出。
整个流程:
- 读入一行的三个字段;
- 按类型识别出编号、名称和经验值;
- 把恢复后的记录存入数组;
- 按编号升序排序;
- 依次输出。
时间复杂度是 ,瓶颈在排序;额外空间复杂度是
。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def is_name(token: str) -> bool:
return token.isalpha() and token.islower()
def solve():
n = int(input())
rows = []
for _ in range(n):
parts = input().split()
user_id = 0
name = ""
exp = ""
for token in parts:
if "." in token:
# 经验值直接按字符串保存,方便保留两位小数格式
exp = token
elif is_name(token):
name = token
else:
user_id = int(token)
rows.append((user_id, name, exp))
rows.sort()
out = []
for user_id, name, exp in rows:
out.append(f"{user_id} {name} {exp}")
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
bool isName(const string& token) {
for (char ch : token) {
if (!(ch >= 'a' && ch <= 'z')) {
return false;
}
}
return !token.empty();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<tuple<long long, string, string>> rows;
rows.reserve(n);
for (int i = 0; i < n; ++i) {
string a, b, c;
cin >> a >> b >> c;
array<string, 3> parts = {a, b, c};
long long userId = 0;
string name;
string exp;
for (const string& token : parts) {
if (token.find('.') != string::npos) {
// 经验值保留原字符串,输出时就不会丢掉末尾的 0
exp = token;
} else if (isName(token)) {
name = token;
} else {
userId = stoll(token);
}
}
rows.push_back({userId, name, exp});
}
sort(rows.begin(), rows.end());
for (const auto& [userId, name, exp] : rows) {
cout << userId << ' ' << name << ' ' << exp << '\n';
}
return 0;
}
- Java
import java.io.*;
import java.util.*;
public class Main {
static class Record {
long id;
String name;
String exp;
Record(long id, String name, String exp) {
this.id = id;
this.name = name;
this.exp = exp;
}
}
static boolean isName(String token) {
if (token.isEmpty()) {
return false;
}
for (int i = 0; i < token.length(); i++) {
char ch = token.charAt(i);
if (ch < 'a' || ch > 'z') {
return false;
}
}
return true;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
List<Record> rows = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
String[] parts = br.readLine().trim().split("\\s+");
long userId = 0;
String name = "";
String exp = "";
for (String token : parts) {
if (token.indexOf('.') >= 0) {
// 原样保留经验值字符串
exp = token;
} else if (isName(token)) {
name = token;
} else {
userId = Long.parseLong(token);
}
}
rows.add(new Record(userId, name, exp));
}
rows.sort(Comparator.comparingLong(r -> r.id));
StringBuilder out = new StringBuilder();
for (Record row : rows) {
out.append(row.id)
.append(' ')
.append(row.name)
.append(' ')
.append(row.exp)
.append('\n');
}
System.out.print(out);
}
}
2. 小兰的内容管理
问题描述
本题是小红书 2025.08.17 第 2 题的原题。
小兰是一位社交媒体平台的内容运营专员,她负责管理平台上的内容推荐系统。平台上有两种主要内容类型:生活分享(用 表示)和知识科普(用
表示)。
现在有 个内容创作者按照一定顺序排列,用长度为
的
字符串
表示他们的内容类型,其中:
- 若
,则第
位创作者主要发布生活分享内容
- 若
,则第
位创作者主要发布知识科普内容
平台会进行无限轮的内容互动推荐,每一轮的规则如下:
- 所有创作者同时向右侧(队列后方)进行内容推荐
- 每个创作者只会向右侧第一个不同类型的创作者推荐内容,如果右侧没有不同类型的创作者,则不会产生推荐
- 每轮所有推荐动作并行计算,然后将所有收到推荐的创作者移出队列,剩余创作者重新排列进入下一轮
这个过程会持续进行,直到不再有推荐发生为止。
请帮助小兰计算整个过程中总共有多少个创作者收到了推荐。
输入格式
第一行包含一个正整数 ,表示创作者的数量。
第二行包含一个长度为 且只由字符 '
' 和 '
' 构成的字符串
,表示创作者的内容类型分布,其中
为从左向右第
位创作者的类型。
输出格式
输出一个整数,表示所有推荐结束后总共有多少个创作者收到了推荐。
样例输入
5
11101
样例输出
2
| 样例 | 解释说明 |
|---|---|
| 样例1 | 第一轮:第3个创作者(' |
数据范围
- 字符串
只包含字符 '
' 和 '
'
题解
这题确实要模拟,但不是在原串上硬做删除,而是先把字符串压成连续段,再模拟“每轮每段发生什么”。
设原串按连续相同字符分段后得到:
其中相邻两段字符一定不同。
现在看一轮操作。
- 第一段左边没有任何字符,所以它不会被评价。
- 从第二段开始,每一段的第一个字符一定会被左边那一段的某个字符评价,因此这一轮都会删掉一个字符。
所以一轮过后会发生三件事:
- 第一段长度不变;
- 其余每一段长度都减一;
- 长度变成零的段消失,消失后如果左右两段字符相同,它们会立刻合并。
这就是整道题的完整演化规则。
于是实现上只要先做一次游程编码,把原串压成若干个 (字符, 长度),然后按轮更新这些连续段即可。
拿样例 11101 举例:
- 初始分段是
111 | 0 | 1; - 第一轮以后,后两段各删掉一个字符,变成
111; - 此时只剩一段,过程结束,总共删掉了
个字符。
为什么这样模拟仍然是 &preview=true)
这一步很关键。
如果看到“按轮模拟”,很容易担心会不会退化成 。实际上不会,因为某一轮如果当前有
段,那么这一轮一定会删掉
个字符,而我们这一轮做的工作量也是
。
把所有轮次加起来,总工作量就是:
而:
就是总共被删除的字符数,不会超过
;
- 轮数同样不会超过
。
因此总时间复杂度是 ,额外空间复杂度是
。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def solve():
n = int(input())
s = input().strip()
# 先做游程编码,把原串压成若干段 (字符, 长度)。
runs = []
i = 0
while i < n:
j = i
while j < n and s[j] == s[i]:
j += 1
runs.append((s[i], j - i))
i = j
ans = 0
# 每轮除了第一段,其余每一段都会删掉一个字符。
while len(runs) > 1:
ans += len(runs) - 1
for i in range(1, len(runs)):
runs[i] = (runs[i][0], runs[i][1] - 1)
# 重建连续段:删掉空段,并把新相邻的同字符段合并。
new_runs = [runs[0]]
for i in range(1, len(runs)):
if runs[i][1] == 0:
continue
if new_runs[-1][0] == runs[i][0]:
char, length = new_runs[-1]
new_runs[-1] = (char, length + runs[i][1])
else:
new_runs.append(runs[i])
runs = new_runs
return ans
print(solve())
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
string s;
cin >> s;
// 先做游程编码,把原串压成若干段 (字符, 长度)。
vector<pair<char, int>> runs;
for (int i = 0; i < n; ) {
int j = i;
while (j < n && s[j] == s[i]) {
++j;
}
runs.push_back({s[i], j - i});
i = j;
}
long long ans = 0;
// 每轮除了第一段,其余每一段都会删掉一个字符。
while (runs.size() > 1) {
ans += (int)runs.size() - 1;
for (size_t i = 1; i < runs.size(); ++i) {
--runs[i].second;
}
// 重建连续段:删掉空段,并把新相邻的同字符段合并。
vector<pair<char, int>> new_runs;
new_runs.reserve(runs.size());
new_runs.push_back(runs[0]);
for (size_t i = 1; i < runs.size(); ++i) {
if (runs[i].second == 0) {
continue;
}
if (new_runs.back().first == runs[i].first) {
new_runs.back().second += runs[i].second;
} else {
new_runs.push_back(runs[i]);
}
}
runs.swap(new_runs);
}
cout << ans << '\n';
return 0;
}
- Java
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
String s = br.readLine();
// 先做游程编码,把原串压成若干段。
List<int[]> runs = new ArrayList<>();
for (int i = 0; i < n; ) {
int j = i;
while (j < n && s.charAt(j) == s.charAt(i)) {
j++;
}
runs.add(new int[]{s.charAt(i), j - i});
i = j;
}
long ans = 0;
// 每轮除了第一段,其余每一段都会删掉一个字符。
while (runs.size() > 1) {
ans += runs.size() - 1L;
for (int i = 1; i < runs.size(); i++) {
runs.get(i)[1]--;
}
// 重建连续段:删掉空段,并把新相邻的同字符段合并。
List<int[]> newRuns = new ArrayList<>();
newRuns.add(runs.get(0));
for (int i = 1; i < runs.size(); i++) {
int[] cur
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力