P9753 [CSP-S 2023] 消消乐 题解

题意

  • 类似于一般的消消乐,连着的两个相同的字母可以被消去,消去后字符串重新拼在一起。求能被完全消去的子串数量。

分析

  • 先想想什么样的字符串可以被全部消去。看完题目,有没有一种括号序列的感觉?
  • c 为任意小写字母,假设字符串 AB 都能被完全消去,那么 c+A+c 以及 A+B 这两种串都是可被完全消去的。
  • 那我们该怎么统计有哪些串可被完全消去呢?
  • 先不管那些长长的、像 A+B+ 这样被拼在一起的串,我们先找出来最短的那些串,然后利用 DP 统计即可。

推导

1. 求出最短的可消去串

  • 定义 lsti 表示以下标 i 为结尾的最短串的区间为 [lsti,i]。特别的,不存在这样的区间时 lsti=0
  • 对于 c+c,也即 stri=stri1 这种情况,lsti=i1
  • 对于 c+A+c 的情况,我们设 l=i1,while 循环判断 strl 是否等于 stri,不等于的话就令 l=lstl1。可以结合下图来理解。
  • 图中每一个方框都是一个 [lsti,i] 的区间,l 按照青色箭头方向转移。当 l 处在最左侧的 x 的位置时找到最小区间,赋值 lsti=l

跳lst找c+A+c

2. 求出可消去串数量

  • 定义 fi 表示以下标 i 为结尾的可消去串数量。那么当 lsti0 时就有转移

fi=flsti1+1

  • 再举一个例子。如图,每一个方框都表示一个区间 [lsti,i]。在求 f9 时,区间 [8,9] 是一个新的可消去串,而且 f7 所对应的每一个可消去串后面接上一个 [8,9] 也仍然是一个可消去串。
  • 于是就得到了式子 f9=f7+1f7 代表的是 f7 所对应的每一个可消去串后面接上一个 [8,9] 组成的串,+1 表示增加 [8,9] 这个串。

求解f_i的例子

3. 答案

  • 很显然,最终答案把所有的 fi 加起来就好了。

考场代码

#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() {
// freopen("game.in", "r", stdin);
// freopen("game.out", "w", stdout);
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]) { //跳lst求解,说实话有点像KMP
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;
}
/*
The moon is shining. Thank you, moon.
*/
  • 感谢观看~
posted @   HappyJaPhy  阅读(354)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示