CF17C Balance

Description

给定一个由 a b c 构成的字符串 S S ,可以选择任意两个相邻字符,有第一个覆盖第二个或用第二个覆盖第一个。求能变出多少不同的字符串,a b c 的个数两两差 1 \leq 1 ,对 51123987 51123987 取模。

1 S 150 1 \leq |S| \leq 150

Solution

序列自动机 + dp。令变出的字符串为 T T ,令它的压缩串为 T T' ,如 aabcccbbaa 的压缩串为 abcba,相当于去重。那么操作后得到的 T T' 一定 S S 子序列,因为想要将一个字母向前向后挪,就要覆盖前面后面的字母,所以不会颠倒 S S 中两个字母的相对顺序,只能保持或让其中一个字母覆盖。

所以 T T' 中的字母是可以与 S S 匹配的。令 f i , a , b , c f_{i,a,b,c} 为, T T' 正与 S i S_i 匹配,此时 T T a b c 出现的次数为 a ,   b ,   c a ,\ b ,\ c 时,有多少个不同的 T T 。Tip: f f 的定义不是到了 S i S_i a b c 出现的次数,而是匹配到 S i S_i 。比如样例 4 abca f 1 , 2 , 0 , 0 = 1 f_{1,2,0,0} = 1 是对的,因为 T T 中的两个 a 都是 S 1 S_1 变出来的。

由于 n 150 n \leq 150 ,所以可以四维枚举 i ,   a ,   b ,   c i ,\ a ,\ b ,\ c 。如果有 a + b + c = n a + b + c = n \land a b ,   a c ,   b c 1 |a-b|, \ |a - c|, \ |b-c| \leq1 ,那么将 f i , a , b , c f_{i,a,b,c} 计入答案。那么如何转移?

S i S_i 为「要去覆盖别的字母」的字母时。那么从它开始覆盖即可,如 $S_i = $ a f i , a + 1 , b , c = f i , a + 1 , b , c + f i , a , b , c f_{i,a+1,b,c} = f_{i,a+1,b,c} + f_{i,a,b,c} 。因为在枚举 a ,   b ,   c a, \ b, \ c ,所以会转移到 f i , a + j , b , c f_{i, a + j, b, c} S i S_i 为「要被覆盖的字母」的字母时,将它转移到「要去覆盖别的字母」的字母即可。

那么如何确定一个 「要去覆盖别的字母」呢,如果枚举它的下标来确定,可能会重复计入答案。比如 S S = caba,其中一个 T T aaaa 即为为 i = 1 S f i , 4 , 0 , 0 = 1 \sum_{i=1}^{|S|} f_{i, 4,0,0} = 1 ,如果枚举下标 i = 1 S f i , 4 , 0 , 0 = 1 \sum_{i=1}^{|S|} f_{i, 4,0,0} = 1 会为 2 2 ,分别在 i = 2 ,   4 i=2, \ 4 。可以发现 i = 2 i = 2 a 在覆盖了 cba i = 4 i = 4 a 就没有必要覆盖 cab 了。

所以可以预处理一个 n x t i , j nxt_{i,j} n x t i , j i S n x t i , j = j nxt_{i,j} \ge i \land S_{nxt_{i,j}} = j 。也就是 i i 后第一个字母 j j 出现的下标,包括 i i 。转移方程如下

f 1 , 0 , 0 , 0 = 1 f_{1,0,0,0} = 1

f n x t i , 0 , a + 1 , b , c = f n x t i , 0 , a + 1 , b , c + f i , a , b , c f_{nxt_{i,0}, a + 1, b, c} = f_{nxt_{i,0}, a + 1, b, c} + f_{i,a,b,c}

f n x t i , 1 , a , b + 1 , c = f n x t i , 1 , a , b + 1 , c + f i , a , b , c f_{nxt_{i,1}, a, b + 1, c} = f_{nxt_{i,1}, a, b + 1, c} + f_{i,a,b,c}

扫描二维码关注公众号,回复: 9016449 查看本文章

f n x t i , 2 , a , b , c + 1 = f n x t i , 2 , a , b , c + 1 + f i , a , b , c f_{nxt_{i,2}, a, b, c + 1} = f_{nxt_{i,2}, a, b, c + 1} + f_{i,a,b,c}

因为每个字符出现的次数不会超过 n + 2 3 \frac{n + 2}{3} ,所以时空复杂度为 O ( n 4 27 ) O(\frac{n^4}{27})

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 150 + 5, p = 51123987;
char s[N];
int f[N][(N + 2) / 3][(N + 2) / 3][(N + 2) / 3], nxt[N][3], n, ans;
int main() {
    scanf("%d%s", &n, s + 1);
    nxt[n + 1][0] = nxt[n + 1][1] = nxt[n + 1][2] = n + 1;
    for(int i = n; i >= 1; i--) {
        nxt[i][0] = nxt[i + 1][0];
        nxt[i][1] = nxt[i + 1][1];
        nxt[i][2] = nxt[i + 1][2];
        if(s[i] == 'a') nxt[i][0] = i;
        if(s[i] == 'b') nxt[i][1] = i;
        if(s[i] == 'c') nxt[i][2] = i;
    }
    f[1][0][0][0] = 1;
    for(int i = 1; i <= n; i++)
        for(int a = 0; a <= (n + 2) / 3; a++)
            for(int b = 0; b <= (n + 2) / 3; b++)
                for(int c = 0; c <= (n + 2) / 3; c++)
                {
                    if(a + b + c == n && abs(a - b) <= 1 && abs(a - c) <= 1 && abs(b - c) <= 1) 
                    	ans = (ans + f[i][a][b][c]) % p; 
                    f[nxt[i][0]][a + 1][b][c] = (f[nxt[i][0]][a + 1][b][c] + f[i][a][b][c]) % p;
                    f[nxt[i][1]][a][b + 1][c] = (f[nxt[i][1]][a][b + 1][c] + f[i][a][b][c]) % p;
                    f[nxt[i][2]][a][b][c + 1] = (f[nxt[i][2]][a][b][c + 1] + f[i][a][b][c]) % p;
                }
    printf("%d\n", ans);
    return 0;
}
发布了28 篇原创文章 · 获赞 38 · 访问量 504

猜你喜欢

转载自blog.csdn.net/qq_39984146/article/details/104209425
今日推荐