【笔试刷题】小红书-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 比较,最后原样输出。

整个流程:

  1. 读入一行的三个字段;
  2. 按类型识别出编号、名称和经验值;
  3. 把恢复后的记录存入数组;
  4. 按编号升序排序;
  5. 依次输出。

时间复杂度是 ,瓶颈在排序;额外空间复杂度是

参考代码

  • 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.172 题的原题。

小兰是一位社交媒体平台的内容运营专员,她负责管理平台上的内容推荐系统。平台上有两种主要内容类型:生活分享(用 表示)和知识科普(用 表示)。

现在有 个内容创作者按照一定顺序排列,用长度为 字符串 表示他们的内容类型,其中:

  • ,则第 位创作者主要发布生活分享内容
  • ,则第 位创作者主要发布知识科普内容

平台会进行无限轮的内容互动推荐,每一轮的规则如下:

  • 所有创作者同时向右侧(队列后方)进行内容推荐
  • 每个创作者只会向右侧第一个不同类型的创作者推荐内容,如果右侧没有不同类型的创作者,则不会产生推荐
  • 每轮所有推荐动作并行计算,然后将所有收到推荐的创作者移出队列,剩余创作者重新排列进入下一轮

这个过程会持续进行,直到不再有推荐发生为止。

请帮助小兰计算整个过程中总共有多少个创作者收到了推荐。

输入格式

第一行包含一个正整数 ,表示创作者的数量。

第二行包含一个长度为 且只由字符 '' 和 '' 构成的字符串 ,表示创作者的内容类型分布,其中 为从左向右第 位创作者的类型。

输出格式

输出一个整数,表示所有推荐结束后总共有多少个创作者收到了推荐。

样例输入

5
11101

样例输出

2
样例 解释说明
样例1 第一轮:第3个创作者('')推荐给第4个创作者(''),第4个创作者('')推荐给第5个创作者(''),共2个创作者收到推荐;移除后剩余前3个创作者形成"",此时全是生活分享类型,不再发生推荐

数据范围

  • 字符串 只包含字符 '' 和 ''

题解

这题确实要模拟,但不是在原串上硬做删除,而是先把字符串压成连续段,再模拟“每轮每段发生什么”。

设原串按连续相同字符分段后得到:

其中相邻两段字符一定不同。

现在看一轮操作。

  • 第一段左边没有任何字符,所以它不会被评价。
  • 从第二段开始,每一段的第一个字符一定会被左边那一段的某个字符评价,因此这一轮都会删掉一个字符。

所以一轮过后会发生三件事:

  1. 第一段长度不变;
  2. 其余每一段长度都减一;
  3. 长度变成零的段消失,消失后如果左右两段字符相同,它们会立刻合并。

这就是整道题的完整演化规则。

于是实现上只要先做一次游程编码,把原串压成若干个 (字符, 长度),然后按轮更新这些连续段即可。

拿样例 11101 举例:

  • 初始分段是 111 | 0 | 1
  • 第一轮以后,后两段各删掉一个字符,变成 111
  • 此时只剩一段,过程结束,总共删掉了 个字符。

为什么这样模拟仍然是

这一步很关键。

如果看到“按轮模拟”,很容易担心会不会退化成 。实际上不会,因为某一轮如果当前有 段,那么这一轮一定会删掉 个字符,而我们这一轮做的工作量也是

把所有轮次加起来,总工作量就是:

而:

  • 就是总共被删除的字符数,不会超过
  • 轮数同样不会超过

因此总时间复杂度是 ,额外空间复杂度是

参考代码

  • 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%内容,订阅专栏后可继续查看/也可单篇购买

互联网刷题笔试宝典 文章被收录于专栏

互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

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