2022.10.22-D 括号序列

题意

一个字符串是“好”的,需要存在一个长度相等合法括号序列,满足:

  • 对于一对匹配的括号,它在字符串上对应位置的字母相同。

现在给出一个仅有小写字母组成的字符串 S,问有多少个子串 是“好”的。

|S|1e6


思路

没有很看懂官方题解对于那个结论的证明,这里提供由 lby 大佬想到的不同解法:

官方题解也有提到,判断一个串是否合法,就是开一个栈,如果当前字母与栈顶字母相同,那么就弹出;否则就将当前字母加入栈中。最后如果栈为,那么就是合法子串。

我们记录 ansi 表示以 i 为左端点的合法串的个数。

当我们进行判断的过程中,一旦栈为空,那么我们其实就可以停止了,这时答案数就是 ansi=ansj+1+1(假如是在 j 处停止的),因为 j+1 是一个相同的子问题。

那么其实就是找到右边最靠近 ij,使得 si,j 是一个合法串。

这显然有 si=sj

所以我们考虑开一个数组 x[i][j],表示 si,k 是合法串,sk+1=j 的最小 k

当我们固定 i 为左端点时,我们就取出 x[i+1][si],记为 r

注意,当 si=si+1 时,r=i+1

这样我们就能有 ansi=ansr+1+1

那怎么更新 x 呢?

很简单,我们将 xr+1 都迁移到 xi 就行了。

然后 x[i][sr+1]=r+1,显然满足我们上面的定义。


代码

#include<bits/stdc++.h> #define LL long long #define max(x...) std::max({x}) #define min(x...) std::min({x}) #define FOR(i, x, y) for(int i = (x); i <= (y); i++) #define ROF(i, x, y) for(int i = (x); i >= (y); i--) inline int rd() { int sign = 1, re = 0; char c = getchar(); while(!isdigit(c)){if(c == '-') sign = -1; c = getchar();} while(isdigit(c)){re = re * 10 + (c - '0'); c = getchar();} return sign * re; } char s[1000005]; int x[1000005][26]; LL ans[1000005], sum; signed main() { #ifdef ONLINE_JUDGE freopen("bracket.in", "r", stdin); freopen("bracket.out", "w", stdout); #endif scanf("%s", s + 1); int n = strlen(s + 1); FOR(i, 1, n + 1) FOR(j, 0, 25) x[i][j] = n + 1; ROF(i, n, 1) { int r = x[i + 1][s[i] - 'a']; if(i < n && s[i + 1] == s[i]) r = i + 1; if(r <= n) { FOR(j, 0, 25) x[i][j] = x[r + 1][j]; if(r < n) x[i][s[r + 1] - 'a'] = r + 1; ans[i] = ans[r + 1] + 1; } sum += ans[i]; } printf("%lld", sum % 998244353); return 0; }

__EOF__

本文作者zuytong
本文链接https://www.cnblogs.com/zuytong/p/16821430.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zuytong  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示