zoukankan      html  css  js  c++  java
  • [动态规划] Codeforces 1499E Chaotic Merge

    题目大意

    给定两个仅由小写字母组成的字符串 (x)(y)

    如果一个序列仅包含 (|x|)(0)(|y|)(1),则称这个序列为合并序列。

    字符串 (z) 初始为空,按如下规则由合并序列 (a) 生成:

    • 如果 (a_i=0),则把 (x) 开头的一个字符加到 (z) 的末尾;
    • 如果 (a_i=1),则把 (y) 开头的一个字符加到 (z) 的末尾。

    两个合并序列 (a)(b) 被认为是不同的,如果存在某个 (i),使得 (a_i eq b_i)

    若一个字符串任意两个相邻位置上的字符都不同,则我们称该字符串是混乱的。

    定义 (f(l_1,r_1,l_2,r_2)) 表示能从 (x) 的子串 (x[l_1,r_1])(y) 的子串 (y[l_2,r_2]) 生成混乱的字符串的不同的合并序列的数量,要求子串非空。

    (sum limits_{1 le l_1 le r_1 le |x| , 1 le l_2 le r_2 le |y|} f(l_1, r_1, l_2, r_2)),答案对 (998244353) 取模。

    ((1leq |x|,|y|leq 1000))

    题解

    直接枚举子串然后算有多少个不同的合并序列肯定不好做,所以我们考虑dp。

    (dp[i][j][0]) 表示以第 (i) 个位置结尾的 (x) 的子串和以第 (j) 个位置结尾的 (y) 的子串进行合并,并且合并后的混乱的字符串以 (x) 的第 (i) 个位置结尾,不同的合并序列的数量。

    (dp[i][j][1]) 表示以第 (i) 个位置结尾的 (x) 的子串和以第 (j) 个位置结尾的 (y) 的子串进行合并,并且合并后的混乱的字符串以 (y) 的第 (j) 个位置结尾,不同的合并序列的数量。

    那么我们只需枚举倒数第二个位置上是什么,同时要满足它和最后一个位置上的字符不同。

    (x[i-1] eq x[i]),则 (dp[i][j][0]+=dp[i-1][j][0])
    (y[j] eq x[i]),则 (dp[i][j][0]+=dp[i-1][j][1])
    (x[i] eq y[j]),则 (dp[i][j][1]+=dp[i][j-1][0])
    (y[j-1] eq y[j]),则 (dp[i][j][1]+=dp[i][j-1][1])

    注意到以上转移必须满足上一个状态中 (x)(y) 的两个子串都非空。但我们可以只取一个字符作为一个子串合并到最后,所以上一个状态的 (x)(y) 是可以为空的,但是空串我们又不计入答案。所以我们维护 (dpx[i]) 表示 (x) 中有多少个以第 (i) 个位置结尾的混乱的子串,(dpy[j]) 表示 (y) 中有多少个以第 (j) 个位置结尾的混乱的子串,则有:

    (x[i] eq x[i-1]),则 (dpx[i]=dpx[i-1]+1),否则 (dpx[i]=1)
    (y[j] eq y[j-1]),则 (dpy[j]=dpy[j-1]+1),否则 (dpy[j]=1)

    (x[i] eq y[j])(dp[i][j][0]+=dpy[j])
    (x[i] eq y[j])(dp[i][j][1]+=dpx[i])

    时间复杂度 (O(|x||y|))

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    const LL MOD = 998244353;
    char x[1005], y[1005];
    LL dp[1001][1001][2], dpx[1001], dpy[1001];
    int n, m;
    
    int main() {
        scanf("%s", x + 1);
        scanf("%s", y + 1);
        n = strlen(x + 1);
        m = strlen(y + 1);
        LL ans = 0;
        for (int i = 1;i <= m;++i) {
            dpy[i] = 1;
            if (y[i - 1] != y[i]) dpy[i] = (dpy[i] + dpy[i - 1]) % MOD;
        }
        for (int i = 1;i <= n;++i) {
            dpx[i] = 1;
            if (x[i - 1] != x[i]) dpx[i] = (dpx[i] + dpx[i - 1]) % MOD;
            for (int j = 1;j <= m;++j) {
                if (x[i] != y[j]) dp[i][j][0] = (dp[i][j][0] + dpy[j]) % MOD;
                if (x[i] != y[j]) dp[i][j][1] = (dp[i][j][1] + dpx[i]) % MOD;
                if (x[i - 1] != x[i]) dp[i][j][0] = (dp[i][j][0] + dp[i - 1][j][0]) % MOD;
                if (y[j] != x[i]) dp[i][j][0] = (dp[i][j][0] + dp[i - 1][j][1]) % MOD;
                if (x[i] != y[j]) dp[i][j][1] = (dp[i][j][1] + dp[i][j - 1][0]) % MOD;
                if (y[j - 1] != y[j]) dp[i][j][1] = (dp[i][j][1] + dp[i][j - 1][1]) % MOD;
                ans = (ans + dp[i][j][0] + dp[i][j][1]) % MOD;
            }
        }
        printf("%I64d
    ", ans);
    
        return 0;
    }
    
  • 相关阅读:
    反射学习系列3反射实例应用
    (转)华为牛人在华为工作十年的感悟!
    利用日志记录所有LINQ的增,删,改解决方案
    qt5摄像头
    opencvcartToPolar笛卡尔坐标转极坐标
    逆矩阵
    方阵的行列式
    qt5右键菜单
    矩阵的转置
    opencvpyrDown降采样和pyrUp升采样
  • 原文地址:https://www.cnblogs.com/AEMShana/p/14583024.html
Copyright © 2011-2022 走看看