一、题目重现
题目描述
小 R 有一个长度为 L、只含小写英文字母的字符串 S。虽然 S 可能很长,但它可以分成 n 个连续的段:第 i 段由 a_i 个相同的小写字母 c_i 构成,把这 n 段依次拼接起来就是 S。相邻两段的字符可以相同。
若一个字符串反转后与原字符串相同,就称它是回文串,例如 aba、abba 是回文串,aabbc 不是。也就是说,第 1 个字符要等于倒数第 1 个,第 2 个要等于倒数第 2 个……
小 R 可以把 S 中任意一个位置的字符改成任意小写英文字母。求最少修改多少个位置,才能让 S 变成回文串。
输入格式
第一行一个正整数 n。接下来 n 行,每行一个正整数 a_i 和一个小写英文字母 c_i。
输出格式
一行一个整数,表示最少修改次数。
输入输出样例
样例 1
3
2 a
2 b
1 c
输出:
2
解释:拼出来是 aabbc。把最后的 c 和倒数第二个 b 都改成 a,得到回文串 aabaa,共改 2 处。
样例 2
2
2 a
3 a
输出 0:拼出来是 aaaaa,本来就是回文串。
样例 3
6
1000000000 a
1000000000 a
1000000000 a
1000000000 b
1000000000 b
1000000000 b
输出:
3000000000
数据范围与约定
- 1 ≤ n ≤ 2×10^5,1 ≤ a_i ≤ 10^9;
- 部分测试点 n 很小但 a_i 达到 10^9,另有测试点 n 与 a_i 同时取到上限;
- 答案可能超过 32 位整数的范围,请使用 64 位整数;
- 原题时限 1.0 秒,内存 1024 MiB。
二、先把样例玩一遍(小学生版)
aabbc 一共 5 个位置,照镜子配对:
| 配对 | 左边 | 右边 | 一样吗 | 要改吗 |
|---|---|---|---|---|
| 第 1 个 ↔ 第 5 个 | a |
c |
不同 | 改 1 处 |
| 第 2 个 ↔ 第 4 个 | a |
b |
不同 | 改 1 处 |
| 第 3 个 ↔ 自己 | b |
b |
相同 | 不用改 |
改成 aabaa,共 2 处,与题面一致。
规律马上冒出来:不同的配对,改 1 处就够;相同的配对,一处都不用改;各对之间互不打扰。所以答案就是「字符不同的配对有多少对」。
为什么改 1 处就够?把其中一个字母改成另一个,这一对就一样了。为什么至少要改 1 处?两个不同的字母,不动其中一个,永远不可能相同。
三、笨办法为什么不行
最直觉的做法:把 aabbc 真的存进一个 string,左右各放一根手指,一个字符一个字符往中间挪。
但 n 最大 2×10^5,每段最多 10^9 个字母,拼起来最长是:
200000 × 1000000000 = 200000000000000 个字母(200 万亿)
别说内存装不下,就算一秒数一亿个,也要数二十多天。这题真正考的是:不把字符串造出来,也能算出答案。
四、妙招:两摞硬币,一次对齐一摞
把左右两边各看成一摞硬币,同一摞上的字母都一样:
- 一次能配多少对?看两边谁先不够:
take = min(左边剩的个数, 右边剩的个数); - 如果两摞字母不同,这
take对每对都要改 1 处,一口气加take,不用一个一个加; - 两边各减掉
take,谁变成 0 就换下一段; - 一直做到左右两只手碰到同一段为止——那是中间段,自己和自己照镜子,字母必然相同,不用改。
打个比方:不是一颗一颗数米,而是「左边这袋 2 斤、右边这袋 1 斤,一次称 1 斤」,一袋一袋过秤。
跟着手算样例 1
段:① 2个a | ② 2个b | ③ 1个c,l 指左、r 指右。
| 轮次 | 左手 | 右手 | 一次配 take | 字母 | 答案 | 谁用完了 |
|---|---|---|---|---|---|---|
| 开始 | ① 剩 2 个 a | ③ 剩 1 个 c | — | — | 0 | — |
| 1 | ① 剩 2 | ③ 剩 1 | min(2,1)=1 |
a ≠ c | 1 | 右边用完,r 退到 ② |
| 2 | ① 剩 1 | ② 剩 2 | min(1,2)=1 |
a ≠ b | 2 | 左边用完,l 进到 ② |
| 结束 | l 与 r 都落在 ② | 同一段自己配自己 | — | b = b | 不加 | 循环结束 |
输出 2。样例 2 同理:两边都是 a,take = 2 但不加,输出 0。
从这里还能看出:相邻两段字母相同,不需要先合并。算法按「还剩多少个」一段段消耗,不合并照样对,别给自己加额外工作。
五、为什么答案一定最少
- 回文要求第 p 个与倒数第 p 个相同,整个字符串被分成若干互不相干的配对(长度为奇数时,最中间那个自己跟自己配,天然相同);
- 一对里字母不同,至少要改 1 个,改 1 个就够,所以每对恰好贡献 0 或 1;
- 不同对之间改谁都不影响别的对,答案就是所有对的贡献相加;
- 两摞硬币只是把「连续一大片相同字母的配对」打包一次性加完,没有多算也没有漏算。
六、一个必须记住的数字
样例 3 里前三段全是 a、后三段全是 b,三段对三段,每段 10 亿个字符都不同:
答案 = 1000000000 × 3 = 3000000000
而 int 最多只装到约 2147483647(21 亿)。3000000000 装进去会溢出变成负数,直接 WA。所以 len、left、right、ans 全部用 long long。
顺带一提:样例 3 用我们的算法只走 3 轮就出答案,30 亿个字符一个都没真的写出来。
七、参考代码(AC)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<long long> len(n);
vector<char> ch(n);
for (int i = 0; i < n; ++i) cin >> len[i] >> ch[i];
// 回文把位置 p 与 L+1-p 配对;每对字符不同只需改一次,且各对独立。
int l = 0, r = n - 1;
long long left = len[l], right = len[r], ans = 0;
while (l < r) {
long long take = min(left, right);
if (ch[l] != ch[r]) ans += take;
left -= take;
right -= take;
if (left == 0) {
++l;
if (l < r) left = len[l];
}
if (right == 0) {
--r;
if (l < r) right = len[r];
}
}
// l == r 时是同一段自己配对,字符必然相同,不产生修改。
cout << ans << '\n';
return 0;
}
八、代码逐段解析
vector<long long> len(n)存每段长度,vector<char> ch(n)存每段字母;长度用long long是因为要参与大数累加。left、right是两只手当前那一摞还剩几个,ans是答案。take = min(left, right)一次配一批;if (ch[l] != ch[r]) ans += take;一口气加,不是+1。- 两个
if分别处理左右换段,不能写成 if/else:两边可能在同一轮同时用完。 while (l < r)而不是l <= r:相遇时是中间段,自己配自己,不用改;里面的if (l < r)保护是防止相遇后还去读不存在的「新一摞」。ios::sync_with_stdio(false); cin.tie(nullptr);给 20 万行读入提速。
九、复杂度分析
每一段只会被「用完」一次,左指针往右走 n 步、右指针往左走 n 步:时间 O(n),内存 O(n)。n 最大 20 万,1 秒绰绰有余。若写成按字符逐个循环,就是 O(L)、L 最大 200 万亿,必超时。
十、易错点清单
- 绝对不要把字符串真的展开(内存和时间都爆)。
ans与len用long long:样例 3 的3000000000会撑爆int。- 一次加
take而不是+1,否则又退化成按字符循环。 - 循环条件写
l < r,写成<=可能在中间段越界。 - 两边同时换段:一轮里左右可能同时用完,两个
if都要写。 - 换段后判断
l < r:相遇了就别再取新段。 - 相邻同字母段不用合并,算法天然支持。
- 文件输入输出:原题要求
string.in/string.out,本文代码改用标准输入输出;OJ 提交时请按题目要求加回freopen。
十一、考点总结
| 考点 | 本题怎么用 |
|---|---|
| 压缩表示 | 只存 n 段,不展开 200 万亿字符 |
| 双指针 | 两头往中间走,每段只消耗一次 |
| 批量计算 | 一次加 take,把 O(L) 压成 O(n) |
| 数据类型 | 答案可达 3×10^9,必须 long long |
一句话记住:回文是照镜子,不同的一对改一个;长串别展开,两摞硬币一次称一批。
本文题面整理自 CCF CSP-J 2026 第二轮(入门组)试题,题目版权归中国计算机学会所有。题解与代码由椰程信奥整理撰写,参考答案仅供参考,最终以 CCF 官方公布为准。



