题意
- 类似于一般的消消乐,连着的两个相同的字母可以被消去,消去后字符串重新拼在一起。求能被完全消去的子串数量。
分析
- 先想想什么样的字符串可以被全部消去。看完题目,有没有一种括号序列的感觉?
- 设 为任意小写字母,假设字符串 和 都能被完全消去,那么 以及 这两种串都是可被完全消去的。
- 那我们该怎么统计有哪些串可被完全消去呢?
- 先不管那些长长的、像 这样被拼在一起的串,我们先找出来最短的那些串,然后利用 DP 统计即可。
推导
1. 求出最短的可消去串
- 定义 表示以下标 为结尾的最短串的区间为 。特别的,不存在这样的区间时 。
- 对于 ,也即 这种情况,。
- 对于 的情况,我们设 ,while 循环判断 是否等于 ,不等于的话就令 。可以结合下图来理解。
- 图中每一个方框都是一个 的区间, 按照青色箭头方向转移。当 处在最左侧的 的位置时找到最小区间,赋值 。

2. 求出可消去串数量
- 定义 表示以下标 为结尾的可消去串数量。那么当 时就有转移
- 再举一个例子。如图,每一个方框都表示一个区间 。在求 时,区间 是一个新的可消去串,而且 所对应的每一个可消去串后面接上一个 也仍然是一个可消去串。
- 于是就得到了式子 , 代表的是 所对应的每一个可消去串后面接上一个 组成的串, 表示增加 这个串。

3. 答案
考场代码
| #include <bits/stdc++.h> |
| #define int long long |
| #define N 2000005 |
| using namespace std; |
| int n, a[N], lst[N], f[N], ans; |
| |
| signed main() { |
| |
| |
| scanf("%lld", &n); |
| char ch = n = 0; |
| while (ch < 'a' || ch > 'z') ch = getchar(); |
| while (ch >= 'a' && ch <= 'z') { |
| a[++n] = ch - 'a'; |
| ch = getchar(); |
| } |
| for (int i = 2; i <= n; i++) { |
| int l = i - 1; |
| while (l > 0 && a[i] != a[l]) { |
| l = lst[l] - 1; |
| } |
| if (l == -1) l++; |
| lst[i] = l; |
| } |
| for (int i = 2; i <= n; i++) { |
| if (lst[i]) { |
| f[i] += f[lst[i] - 1] + 1; |
| } |
| } |
| for (int i = 1; i <= n; i++) { |
| ans += f[i]; |
| } |
| printf("%lld", ans); |
| return 0; |
| } |
| |
| |
| |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!