2022.10.22-D 括号序列

题意

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

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

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

\(|S| \le 1e6\)


思路

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

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

我们记录 \(ans_i\) 表示以 \(i\) 为左端点的合法串的个数。

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

那么其实就是找到右边最靠近 \(i\)\(j\),使得 \(s_{i,j}\) 是一个合法串。

这显然有 \(s_i=s_j\)

所以我们考虑开一个数组 \(x[i][j]\),表示 \(s_{i,k}\) 是合法串,\(s_{k+1}=j\) 的最小 \(k\)

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

注意,当 \(s_i=s_{i+1}\) 时,\(r=i+1\)

这样我们就能有 \(ans_i=ans_{r+1}+1\)

那怎么更新 \(x\) 呢?

很简单,我们将 \(x_{r+1}\) 都迁移到 \(x_i\) 就行了。

然后 \(x[i][s_{r+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;
}
posted @ 2022-10-24 14:46  zuytong  阅读(25)  评论(0编辑  收藏  举报