[CSP-S2023T2]消消乐

题目链接

[CSP-S2023T2]消消乐

题目描述

小 L 现在在玩一个低配版本的消消乐,该版本的游戏是一维的,一次也只能消除两个相邻的元素。

现在,他有一个长度为 n n n 且仅由小写字母构成的字符串。我们称一个字符串是可消除的,当且仅当可以对这个字符串进行若干次操作,使之成为一个空字符串。

其中每次操作可以从字符串中删除两个相邻的相同字符,操作后剩余字符串会拼接在一起。

小 L 想知道,这个字符串的所有非空连续子串中,有多少个是可消除的。

输入格式

输入的第一行包含一个正整数 n n n,表示字符串的长度。

输入的第二行包含一个长度为 n n n 且仅由小写字母构成的字符串,表示题目中询问的字符串。

输出格式

输出一行包含一个整数,表示题目询问的答案。

输入输出样例 #1

输入 #1

8
accabccb

输出 #1

5

说明/提示

【样例 1 解释】

一共有 5 5 5 个可消除的连续子串,分别是 ccaccaccbccbaccabccb

【数据范围】

对于所有测试数据有: 1 ≤ n ≤ 2 × 1 0 6 1 \le n \le 2 \times 10^6 1n2×106,且询问的字符串仅由小写字母构成。

测试点 n ≤ n\leq n特殊性质
1 ∼ 5 1\sim 5 15 10 10 10
6 ∼ 7 6\sim 7 67 800 800 800
8 ∼ 10 8\sim 10 810 8000 8000 8000
11 ∼ 12 11\sim 12 1112 2 × 1 0 5 2\times 10^5 2×105A
13 ∼ 14 13\sim 14 1314 2 × 1 0 5 2\times 10^5 2×105B
15 ∼ 17 15\sim 17 1517 2 × 1 0 5 2\times 10^5 2×105
18 ∼ 20 18\sim 20 1820 2 × 1 0 6 2\times 10^6 2×106

特殊性质 A:字符串中的每个字符独立等概率地从字符集中选择。

特殊性质 B:字符串仅由 ab 构成。

算法思想(动态规划)

状态表示

f[i]表示字符串 s s s中以i位置作为结尾的可消除的子串数量,答案即为 ∑ i = 1 n f [ i ] \sum_{i=1}^nf[i] i=1nf[i]

状态计算

如果存在位置j( j < i j<i j<i),使得子串 s [ j , i ] s[j,i] s[j,i]为可消除子串,如下图所示:
在这里插入图片描述
那么f[i] = f[j - 1] + 1。注意,这里的j是所有满足条件的位置中的最大值。

为了避免计算f[i]时枚举j,不妨将每次得到的j保存到g[i]中。在计算f[i]时:

  • 首先令j = i - 1
  • 如果s[j] != s[i],将j = g[j] - 1,即跳过以j结尾的合法子串,直到找到可消除的位置j,或者跳过整个字符串。跳转过程如下图所示:
    在这里插入图片描述- 如果找到可消除的位置j,令g[i] = j,计算f[i] = f[j - 1] + 1

时间复杂度

因为外层 for 循环执行 n n n 次,而 while 循环的总执行次数最多为 n n n 次,所以平均下来,每次外层循环中 while 循环的执行次数为常数次。因此时间复杂度为 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 5;
char s[N], stk[N];
int f[N], g[N];
int main()
{    
    int n;
    long long ans = 0;
    cin >> n >> s + 1;
    for(int i = 1; i <= n; i ++)
    {
        int j = i - 1;
        while(j >= 1 && s[j] != s[i]) j = g[j] - 1;
        if(j >= 1)
        {
            g[i] = j;
            f[i] = f[j - 1] + 1;
        }
        ans += f[i];
    }
    cout << ans << endl;
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值