题单 · 习题解析

洛谷 P3375 【模板】KMP

★★★ 它就是[第 48 章正文](/ch/48-kmp/)那道题的原题 ⇒ 这一页一个字都不重讲 KMP,而是兑现题单那句注解「**交之前先把两边的约定对一遍**」:会咬人的三处**全在「约定」和「输出」上,一处都不在 KMP 上** —— 位置是 **1 基**(本书一律 0 基)/ 位置**一行一个、不打次数** / border 那一行是「每个**前缀**的最长 border」,恰好就是 0 基 `nxt[0..m-1]`,⚠ 而所有人都会在这儿犹豫一下、一犹豫就右移一格;★★★ 而第四处**题面一个字都没提**:**输出量** —— 输出最大的形状不是「串最长」是「**命中最多**」(`s₁` = 10⁶ 个 A、`s₂` = 一个 A ⇒ 每个位置都命中),实测 **6 888 896 字节**;五种打法里 `endl` **247 ms**、` ` 28、关同步 25、`printf` 30、自写缓冲 **8.8** ⇒ ★★ **`endl` → ` ` 值 8.8 倍,和 [P3383](/sol/p3383/) 量到的一模一样**,而**关同步在输出这一侧只值 1.1 倍**(读入那一侧是 6.2 倍);⚠⚠ 而「所以 endl 版会 TLE」量完要收回去 —— 端到端 **0.25 秒 / 时限 1 秒,它能过**,只是把正解 **100 倍**的余量吃到只剩 **4 倍**;★ 八格「触发 ≡ 抓获」一个不差,而 `endl` 那一列**四档全 0**(答案永远对,对拍看不见);⚠ 顺手写的那一档在**验零**:26 个大写字母随机,300 轮里只有 **8 轮** `s₂` 真的出现过;★ 官方唯一那组样例把两个约定错法**全打死**(约定错是「每一组都错」型);⚠⚠ 外加一条对照:**这道题的官方样例输出行末带一个空格**(评测机忽略行末空白),而[第 47 章 P1598](/sol/p1598/) 把「不许有多余空格」写了两遍 ⇒ **行末空格要不要紧是每道题各自的规定**,可我们自己的对拍仍然要逐字节比;★ 顺带:题面子任务 0+1 让**朴素匹配稳拿 70 分**

原题:洛谷 P3375出自 第 48 章 KMP:失配的时候,i 一步都不用退 的题单题面本地存档:2026-09-08
⚠ 先自己写一遍,再往下看

这一页不是标准答案,是一条阶梯:把大多数人真实会写出的第一版摆出来, 看它错在哪、慢在哪,再一步一步改。所以它对「已经自己动手撞过一次墙」的人最有用。
还没写过的话,先去写 —— 写出来的哪怕是错的,也比直接看这一页收获大。

0题目原文(本地存了一份)

为什么要在这儿抄一份题面

原题在洛谷上(页头有链接)。别人的网站不归我们管,打不开、改版、题号调整都可能发生。 所以每个解析页都把题面转录一份存在本地,跟着仓库一起进版本库。

转录自洛谷 P3375,日期见页头。两边不一致时信原站。

题目描述

给出两个字符串 s₁s₂,若 s₁ 的区间 [l, r] 子串与 s₂ 完全相同,则称 s₂s₁ 中出现了, 其出现位置为 l。现在请你求出 s₂s₁ 中所有出现的位置。

定义一个字符串 sborders 的一个s 本身的子串 t, 满足 t 既是 s 的前缀,又是 s 的后缀。 对于 s₂,你还需要求出对于其每个前缀 s' 的最长 border t' 的长度。

输入格式

第一行为一个字符串,即为 s₁。第二行为一个字符串,即为 s₂

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s₂s₁ 中出现的位置。

最后一行输出 |s₂| 个整数,第 i 个整数表示 s₂长度为 i 的前缀的最长 border 长度。

数据规模与约定

本题采用多测试点捆绑测试,共有 4 个子任务。

  • Subtask 0(30 分):|s₁| ≤ 15|s₂| ≤ 5
  • Subtask 1(40 分):|s₁| ≤ 10⁴|s₂| ≤ 10²
  • Subtask 2(30 分):无特殊约定。
  • Subtask 3(0 分):Hack。

对于全部的测试点,保证 1 ≤ |s₁|, |s₂| ≤ 10⁶s₁s₂ 中均只含大写英文字母。

时限 1 秒,内存 524288 KB(512 MB)。

输入输出样例

输入

ABABABC
ABA

输出

1
3
0 0 1

ABAABABABC 里出现在位置 13(★ 1 基,而且两次是重叠的)。

最后一行:ABA 的三个前缀 A / AB / ABA 的最长 border 长度分别是 0 / 0 / 1ABAA 既是前缀也是后缀)。

样例解释里的那张图(原站的图,本地存了一份):

P3375 样例解释

1★★ 这一页的主线:会咬人的三处全在「约定」和「输出」上,一处都不在 KMP 上

★★★ 「正文用过这道题」不等于「解析页没得写」

这道题就是第 48 章正文那道题的原题 —— 算法一个字都不用再讲一遍 (next 怎么算、失配时 i 为什么不退、命中后为什么是 j = nxt[j-1],正文第 4~8 步全讲透了)。

★ 而第 48 章的题单在这道题后面挂了一句注解:

「⚠ 它的下标从 1 开始,next 的输出口径也和本书不同 —— 交之前先把两边的约定对一遍」。

这一页就是把那句话兑现成三个错法、一张输出账。 三处差别:

本书正文 这道题的题面
出现位置 0 基 1 基(样例是 1 和 3)
位置怎么输出 先打次数,再一行打完 一行一个,不打次数
next 那一行 p 的 next 数组 「每个前缀的最长 border」——★ 恰好就是 0 基的 nxt[0..m-1]

⚠ 而真正会挂人的第四处,题面里一个字都没提:输出量。第 ④ 步整节在算这笔账。

p3375.cpp★ 正解:KMP + 自己写输出缓冲
// P3375【模板】KMP —— 正解:KMP + 自己写输出缓冲
//
// ★★ 这道题就是[第 48 章正文](/ch/48-kmp/)那道题的原题,**可它的约定和本书不一样**,
// 三处都要对一遍(题单注解点的就是这件事):
// ① 出现位置是 **1 基**(题面:「其出现位置为 l」,样例里 ABA 在 ABABABC 中是 1 和 3)——
// 本书正文一律 0 基 ⇒ 照抄要 **+1**(见 p3375Base.cpp);
// ② 第二行要的是「s2 的**每个前缀**的最长 border 长度」,一共 |s2| 个数
// —— 恰好就是 0 基 nxt[0..m-1],⚠ 但错位一格是最容易犯的(见 p3375Border.cpp);
// ③ 位置**按从小到大、一行一个**输出,不输出个数 —— 和正文那种「先打次数再打一行」完全不同。
//
// ⚠⚠ 而这道题真正的新关卡,正文一次都没量过:**输出**。
// 顶格 s1 = 10⁶ 个 A、s2 = 一个 A ⇒ **10⁶ 行位置,约 6.9 MB**(见 p3375Out.cpp)。
// ⇒ `cout << x << endl` 会 flush 一百万次,当场 TLE(见 p3375Endl.cpp)。
#include <cstdio>
#include <cstring>
using namespace std;
static const int MAXN = 1000006;
static char s1[MAXN], s2[MAXN];
static int nxt[MAXN];
static char obuf[1 << 22]; // 4 MiB 输出缓冲:顶格输出约 6.9 MB,分两次冲出去
static int opos = 0;
static inline void flushOut() { fwrite(obuf, 1, (size_t)opos, stdout); opos = 0; }
static inline void putc_(char c) { if (opos == (int)sizeof(obuf)) flushOut(); obuf[opos++] = c; }
static inline void writeInt(int x, char tail) {
if (opos + 14 > (int)sizeof(obuf)) flushOut();
if (!x) obuf[opos++] = '0';
else { char t[12]; int k = 0; while (x) { t[k++] = (char)('0' + x % 10); x /= 10; }
while (k) obuf[opos++] = t[--k]; }
putc_(tail);
}
int main() {
if (scanf("%s%s", s1, s2) != 2) return 0;
int n = (int)strlen(s1), m = (int)strlen(s2);
/* nxt[i] = s2[0..i] 的最长 border 长度(0 基)—— 正文第 5 步那五行 */
nxt[0] = 0;
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && s2[i] != s2[j]) j = nxt[j - 1];
if (s2[i] == s2[j]) j++;
nxt[i] = j;
}
/* 匹配:i 一步都不退 */
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && s1[i] != s2[j]) j = nxt[j - 1];
if (s1[i] == s2[j]) j++;
if (j == m) {
writeInt(i - m + 2, '\n'); // ★ +1 变 1 基,再 −m+1 回到起点 ⇒ i-m+2
j = nxt[j - 1]; // ⚠ 不能归零,否则漏掉重叠出现
}
}
for (int i = 0; i < m; i++) writeInt(nxt[i], i + 1 == m ? '\n' : ' ');
flushOut();
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
p3375Brute.cpp★ 参照物:朴素匹配 + 按定义枚举 border
★ 顺带一笔:这道题的暴力稳拿 70 分

题面的子任务分档就是出题人递过来的工具(第 43 章 P1313 那条):

  • Subtask 0(30 分):|s₁| ≤ 15 ⇒ 随便写;
  • Subtask 1(40 分):|s₁| ≤ 10⁴|s₂| ≤ 10² ⇒ 朴素匹配最多 10⁶ 次比较,秒过。

上面那份 p3375Brute.cpp 直接交上去就有 70 分,而它一行 KMP 都没有。

2⚠ 错法一:位置照抄本书的 0 基

★ 触发条件是一句话:s₂ 至少出现过一次

本书正文(以及第 47 章 P1308)里,位置一律从 0 数; 而这道题的题面写的是「若 s₁ 的区间 [l, r] 子串与 s₂ 完全相同……其出现位置为 l」, 样例给的是 1 和 3

⇒ 少一个 +1每一个位置都小 1 —— 而它只在「真的有出现」时才现形: 一次都没出现时两边都只打 border 那一行,逐字节相同(那是能证的 0)。

p3375Base.cpp✗ 错法一:位置是 0 基
// P3375 ✗ 错法一:位置照抄本书的 0 基,忘了题面是 1 基
//
// ⚠ 这是「把本书的写法搬到真题上」最典型的一跤:正文里位置一律从 0 数
// (第 47 章 P1308 也是「位置从 0 开始」),而这道题的题面写的是
// 「若 s1 的区间 [l, r] 子串与 s2 完全相同……其出现位置为 l」—— 官方样例是 1 和 3。
// ★ 触发条件干净得可以写成一句话:**只要 s2 在 s1 里出现过至少一次**。
// ⇒ 一次都没出现时它和正解逐字节相同(那一档是能证的 0)。
#include <cstdio>
#include <cstring>
using namespace std;
static const int MAXN = 1000006;
static char s1[MAXN], s2[MAXN];
static int nxt[MAXN];
int main() {
if (scanf("%s%s", s1, s2) != 2) return 0;
int n = (int)strlen(s1), m = (int)strlen(s2);
nxt[0] = 0;
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && s2[i] != s2[j]) j = nxt[j - 1];
if (s2[i] == s2[j]) j++;
nxt[i] = j;
}
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && s1[i] != s2[j]) j = nxt[j - 1];
if (s1[i] == s2[j]) j++;
if (j == m) { printf("%d\n", i - m + 1); j = nxt[j - 1]; } // ⚠ 少了那个 +1
}
for (int i = 0; i < m; i++) printf("%d%c", nxt[i], i + 1 == m ? '\n' : ' ');
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果

3⚠ 错法二:border 那一行错位一格

★★ 「第 i 个前缀」到底对应 nxt[i] 还是 nxt[i-1]

题面要的是:i 个整数 = 长度为 i 的前缀的最长 border 长度i = 1..m)。

而 0 基的 nxt[i] 说的是「s₂[0..i] 的最长 border」,也就是长度 i+1 的前缀直接输出 nxt[0]nxt[1]、…、nxt[m-1] 就是对的。

⚠ 可这一步所有人都会犹豫一下,一犹豫就写成「先补一个 0,再输出 nxt[0..m-2]」——整行右移一格。 ★ 触发条件:移位之后那一行和原来不同 (⇒ nxt 全是 0 时它反而对 —— 而那正是顺手写的随机数据的常态,见第 ⑤ 步档 0 的 27 / 300)。

p3375Border.cpp✗ 错法二:border 行右移一格

4★★★ 第四处题面没提的:输出量 —— 顶格 6.89 MB、10⁶ 行

p3375Out.cpp★ 五种打法,同一份顶格输出各跑一遍
⚠ 正文那 6.89 MB 写进一个真文件(freopen —— 评测机就是这么干的),量完再把 fd 1 换回来打报告。
// P3375 —— 「输出」那一关到底值多少:五种打法,同一份顶格数据各跑一遍。
// ./p3375Out <打法> <输出文件> 打法 ∈ endl / nl / nosync / printf / buf
// 报告打在标准输出上,**正文那 6.9 MB 写进 <输出文件>**。
//
// ⚠⚠ 为什么一定要写进**文件**,而不是让它打到管道里(2026-09-07 在 [P1308](/sol/p1308/) 上撞出来的):
// 管道那一头是谁、读得多快,**会被一起量进来**。评测机做的是「stdout 重定向到一个文件」,
// 所以这里也用 `freopen`,量完再 `dup2` 把 fd 1 换回来打报告。
// ⇒ ★★ **量什么都要先问一句「这个数里有没有别人的份」。**
//
// ★ 顶格形状挑的是「输出最多」的那一种:s1 = 10⁶ 个 A、s2 = 一个 A
// ⇒ **每一个位置都命中**,要打 10⁶ 行。
#include <bits/stdc++.h>
#include <unistd.h>
using namespace std;
static const int N = 1000000;
static vector<int> pos; // 所有命中位置(1 基)
static char obuf[1 << 22];
static int opos = 0;
static inline void flushOut() { fwrite(obuf, 1, (size_t)opos, stdout); opos = 0; }
static inline void writeInt(int x) {
if (opos + 14 > (int)sizeof(obuf)) flushOut();
if (!x) obuf[opos++] = '0';
else { char t[12]; int k = 0; while (x) { t[k++] = (char)('0' + x % 10); x /= 10; }
while (k) obuf[opos++] = t[--k]; }
obuf[opos++] = '\n';
}
int main(int argc, char** argv) {
string mode = argc > 1 ? argv[1] : "buf";
const char* out = argc > 2 ? argv[2] : "/dev/null";
/* 顶格:s1 全是 A、s2 只有一个 A ⇒ 命中 10⁶ 次(KMP 那部分这里不是重点,直接列出位置)*/
pos.reserve(N);
for (int i = 1; i <= N; i++) pos.push_back(i);
int saved = dup(1);
if (!freopen(out, "w", stdout)) return 1;
auto t0 = chrono::steady_clock::now();
if (mode == "endl") {
for (int p : pos) cout << p << endl; // ⚠ 每行 flush
} else if (mode == "nl") {
for (int p : pos) cout << p << '\n';
} else if (mode == "nosync") {
ios::sync_with_stdio(false);
cout.tie(nullptr);
for (int p : pos) cout << p << '\n';
} else if (mode == "printf") {
for (int p : pos) printf("%d\n", p);
} else {
for (int p : pos) writeInt(p);
flushOut();
}
cout.flush();
fflush(stdout);
auto t1 = chrono::steady_clock::now();
double ms = chrono::duration<double, milli>(t1 - t0).count();
long long bytes = ftell(stdout);
dup2(saved, 1);
close(saved);
clearerr(stdout);
printf("%s,%.1f,%lld\n", mode.c_str(), ms, bytes);
return 0;
}
点一下即可编辑
输出
点「运行 ▶」看结果
★★ 先算一句:这道题的输出能有多大

输出量最大的形状不是「串最长」,是「命中最多」:

   s1 = 10⁶ 个 A          s2 = 一个 A
   ⇒ 每一个位置都命中 ⇒ 要打 10⁶ 行位置

实测输出 6 888 896 字节(6.57 MiB) —— 而题面对输出量只字未提。

五种打法(A 机:WSL2 6.18 / 8 核 / 8 GB,2026-09-08,独占;同一份 10⁶ 行,各跑 3 次取中位数):

打法 耗时 相对最快
cout << x << endl 247 ms 28 倍
cout << x << '\n' 28 ms 3.2 倍
关同步 + '\n' 25 ms 2.8 倍
printf("%d\n", x) 30 ms 3.4 倍
★ 自己写 4 MiB 缓冲 + fwrite 8.8 ms 1.0

★★ endl'\n' 值 8.8 倍 —— 和第 41 章 P3383 量到的一模一样(那页也是 8.8 倍)。 ⇒ endl 的问题从来不是换行,是每一行都 flush 一次,这里是一百万次。 ★ 而关同步在输出这一侧几乎不值钱(28 → 25 ms,1.1 倍)—— ⚠ 对照读入那一侧的 6.2 倍(P3383):并排放着的两条建议,分量差一个数量级。

⚠⚠ 而「所以 endl 版会 TLE」这句话,量完之后要收回去

端到端跑一遍顶格(读 1 MB + KMP + 写 6.89 MB):

版本 顶格耗时 时限 1 秒
★ 正解 0.01 秒 余量 100 倍
endl 0.25 秒 它能过,余量只剩 4 倍

⇒ ★★★ 「肯定超时」又一次被打回第 41 章第 42 章第 46 章 都中过)—— 正确的说法是:它不是过不了,是把 100 倍的余量吃到只剩 4 倍。 ⚠ 而这 4 倍是在本机量的,评测机更慢、输出还可能过管道 —— 赌不起。

★★ 顺带一条对拍纪律:endl 版和正解的输出逐字节相同, 四档 1200 轮一次都没被抓到(下一步那张表的最后一列全是 0)。 ⇒ 第 48 章第 9 步那条在这儿换了个主语又成立一次: 只影响速度、不影响答案的写法,对拍原理上看不见 —— 这一次它长在输出上。

p3375Endl.cpp✗ 错法三:用 endl 一行一行打(答案全对)

5★ 对拍:八格「触发 ≡ 抓获」,而顺手写的那一档在验零

p3375Gen.cpp★ 生成器:档 1 起保证 s₂ 真的出现
// P3375 的生成器:./p3375Gen 种子 [档位]
//
// ★ 三个待测版本各靠什么现形:
// · p3375Base(位置忘了 +1) → **s2 在 s1 里至少出现一次**;
// · p3375Border(border 行右移一格)→ **移位之后那一行和原来不同**;
// · p3375Endl(用 endl 打) → ⚠ **答案永远对,对拍原理上抓不到**(只能量时间)。
//
// 档位:
// 0 ★ 顺手写的:26 个大写字母随机,|s1| = 20~40、|s2| = 2~5
// ⚠ 这一档多半**一次都不出现**(随机 26 个字母里撞上一个长度 3 的串,概率约 1/17576)
// 1 ⚠ 保证出现:先摇 s2,再把它塞进 s1 的随机位置(1~3 处)
// 2 ⚠ 字母表压到 2 个(A / B)⇒ border 非零、重叠出现满地都是
// 3 ★ 最终档:小字母表 + 周期串(s2 = 一小段重复若干次)
//
// ⚠ rng() 一律先落到具名变量再传参([第 24 章 P1776](/sol/p1776/) 那一跤)。
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char** argv) {
unsigned seed = argc > 1 ? (unsigned)atoi(argv[1]) : 1;
int mode = argc > 2 ? atoi(argv[2]) : 0;
mt19937 rng(seed * 1000003u + 20260908u);
int alpha = (mode >= 2) ? 2 : 26;
auto pick = [&]() { unsigned r = rng() % (unsigned)alpha; return (char)('A' + r); };
unsigned r1 = rng() % 21u;
int n = 20 + (int)r1;
unsigned r2 = rng() % 4u;
int m = 2 + (int)r2;
string b;
if (mode == 3) { // 周期串:一小段重复若干次
unsigned rp = rng() % 3u;
int p = 1 + (int)rp;
unsigned rk = rng() % 3u;
int k = 2 + (int)rk;
string unit;
for (int i = 0; i < p; i++) unit.push_back(pick());
for (int i = 0; i < k; i++) b += unit;
m = (int)b.size();
} else {
for (int i = 0; i < m; i++) b.push_back(pick());
}
string a;
for (int i = 0; i < n; i++) a.push_back(pick());
if (mode >= 1) { // 保证出现:塞 1~3 处
unsigned rc = rng() % 3u;
int times = 1 + (int)rc;
for (int t = 0; t < times && m <= n; t++) {
unsigned rp = rng() % (unsigned)(n - m + 1);
for (int k = 0; k < m; k++) a[(size_t)rp + (size_t)k] = b[(size_t)k];
}
}
printf("%s\n%s\n", a.c_str(), b.c_str());
return 0;
}
点一下即可编辑
输出
点「运行 ▶」看结果

四档 × 300 轮(正解 vs 朴素参照物:1200 轮 0 组不一致):

档位 有出现的轮数 ✗ 位置 0 基 ✗ border 右移 endl
0 ★ 顺手写的(26 个大写字母随机) 8 8 27 0
1 ⚠ 保证 s₂ 出现(塞进 s₁ 里) 300 300 27 0
2 ⚠ 字母表压到 2 个 300 300 247 0
3 ★ 最终档(小字母表 + 周期串) 300 300 300 0
★★★ 三条读得出来的结论
  1. ★★★ 顺手写的那一档在验零 —— 26 个大写字母里随机造一个长度 2~5 的 s₂, 300 轮里只有 8 轮它真的出现在 s₁ 里(一个长度 3 的串撞上的概率约 1/17576)。 ⇒ 另外 292 轮所有版本一起只打 border 那一行,对拍记「通过」。 ⇒ 第 47 章 P1071 那条刚立的规矩,换一道题又成立一次: 先算一句「正解会不会答出东西来」,低了就反着造。

  2. ★★ 两个「约定」错法的触发条件方向相反 —— 「位置 0 基」要的是有出现 (小字母表一抓一个准),「border 右移」要的是 nxt 不全为 0 (随机 26 个字母时 nxt 几乎全是 0 ⇒ 只有 27 / 300)。 ⇒ ★ 一个旋钮(字母表大小)同时把两列往上推,而它们原来卡在不同的地方

  3. 八格「触发 ≡ 抓获」一个不差(前两列),而第三列是结构性的全 0endl 版的答案永远对。

6★ 官方那唯一一组样例:两个「约定」错法全打死

★★ 而这正是那条规律的正面:约定错是「每一组都错」型
✗ 位置 0 基 ✗ border 右移 endl
官方样例 ABABABC / ABA (打出 0 和 2) (打出 0 0 0 放过(答案相同)

★ 本书连着量了十几道题的那条规律 —— 官方样例是个「一测就死」的过滤器:它挡住「每一组都错」的,放过「偶尔才错」的 —— 在这一页是正面成立的:两个约定错法每一组都错,所以一交样例就死。

⚠ 而它对第三个错法(endl完全无能为力,因为那个错法的答案是对的 (第 20 章 P5019 那条:样例这个过滤器筛的是「答案错」)。

⚠⚠ 外加一条只有把两页放在一起才看得见的: 这道题的官方样例输出,最后一行末尾是带一个空格的0 0 1 )—— 也就是说它的评测机忽略行末空白。 而第 47 章 P1598 的题面把「任何一行末尾不要打印多余空格」写了两遍。 ⇒ ★★ 「行末空格要不要紧」不是一个能背的经验,是每道题各自的规定 —— ⚠ 但我们自己的对拍仍然要逐字节比(P1598 那页量过:一 strip,一整类 bug 当场消失)。

7★ 哪一版就已经能过了

★ 算法早就写完了,剩下的全是「对约定」和「算输出量」
版本 结果 说明
p3375.cpp AC 顶格 0.01 秒 / 时限 1 秒,余量 100 倍
p3375Brute.cpp 70 分 Subtask 0 + 1(10⁴ × 10² = 10⁶ 次比较)
✗ 位置 0 基 WA 样例就死
✗ border 右移 WA 样例就死
endl 能过(0.25 秒) 余量从 100 倍掉到 4 倍 —— 赌不起

⇒ ★★ 一句话带走:模板题的模板部分是最不容易错的那部分。 这一页的三个坑(1 基 / border 口径 / 输出量), 一个来自「把别处的写法搬过来」,一个来自「题面那句话读了两遍还是没读准」, 一个来自「题面根本没提」—— ⇒ 交模板题之前,把输入输出格式那两段逐字读一遍, 再乘一句「最多要打多少字节」。