[解题记录] NOI Online 2022 入门组 字符串

NOI Online 2022 入门组 字符串#

题意简述#


给定两个字符串 ST 和一个初始为空的字符串 R,其中 S 长度为 n,且只由 0, 1, - 三种字符构成(注:这里的第三种字符是减号), 然后进行 n 次操作, 每次会取出 S 的第一个字符(记为 c),并将其从 S 中删去。如果 c=-,则要删去 R 的开头字符或结尾字符(数据保证删去后 R 不为空)。将 c 加入到 R 的末尾。问将 R 变成 T 的方案数。

解题思路#


首先不难想到一个 O(2n/2) 的爆搜,就是枚举遇到减号时的决策,这样可以拿 30pts

看到题目的数据范围,以及题目要我们求方案数,就联想到 DP, 我们定义状态 f[i][j][x][y] 表示 S 操作到第 i 个, 成功匹配了 T 的前 j 个数, R 前面要删除 x 个数,后面要删除 y 个数的方案,那么状态转移方程就是 :

  1. Si=f[i+1][j][x1][y]+=f[i][j][x][y]
  2. Sif[i+1][j][x][l+1]+=f[i][j][x][y]
  3. SiSi=Tj1l=0f[i+1][j+1][x][y]+=f[i][j][x][y]

复杂度是 O(n3m) 的,不能通过所有数据,但是我们发现其实最后一维完全没有必要记录,因为每次操作完后,R 的字符数是确定的,所以只要知道前面有几个要删除,成功匹配了几个,状态就是唯一确定的,去掉一维后复杂度就变成 O(n2m) 的,但是实现一下就会发现 DP 要处理的细节非常多,我们所以可以写 记忆化搜索

Code#


DP

#include <bits/stdc++.h>
using namespace std;
const int N = 410, mod = 1e9 + 7;
int f[N][N][N], n, m, T;
char s[N], t[N];
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
        memset(f, 0, sizeof(f));
        int sum = 0;
        f[0][0][0] = 1;
        for (int i = 1; i <= n; ++i) {
            if (s[i] == '-') sum ++;
            for (int j = 0; j <= m; ++j) {
                for (int k = 0; k <= n; ++k) {//结尾
                    int l = i - 2 * sum - k - j;
                    if (l < 0) continue;
                    if (s[i] == '-') f[i][j][k] = (f[i - 1][j][k + 1] + f[i - 1][j][k]) % mod;//删左和删右
                    else {
                        if (k != 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1]) % mod;//放后面
                        else if (s[i] == t[j]) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k]) % mod;
                        if (!k && !j) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k]) % mod;
                    }
                }
            }
        }
        printf("%d\n", f[n][m][0]);
    }
    return 0;
}

dfs

#include <bits/stdc++.h>
using namespace std;
const int N = 410, mod = 1e9 + 7;
int f[N][N][N], n, m, T;
char s[N], t[N];
int dfs(int i, int j, int x, int y) {//表示操作到s[i],匹配成功j个,前面要删x个,后面删除y个走下去成功的方案
    if (i > n) return j == m && !x && !y;
    if (~f[i][j][x]) return f[i][j][x];
    int val = 0;
    if (s[i] == '-') {
        if (x) val = (val + dfs(i + 1, j, x - 1, y)) % mod;//删前面的
        if (y) val = (val + dfs(i + 1, j, x, y - 1)) % mod;//删后面的
    }
    else {
        val = (val + dfs(i + 1, j, x, y + 1)) % mod;//直接放到后面
        if (!j && !y) val = (val + dfs(i + 1, j, x + 1, y)) % mod;//如果中间和后面都是空的
        if (s[i] == t[j + 1] && !y) val = (val + dfs(i + 1, j + 1, x, y)) % mod;//如果匹配成功
    }
    return f[i][j][x] = val;
}
int main() {
    scanf("%d", &T);
    while (T--) {
        memset(f, -1, sizeof(f));
        scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
        printf("%d\n", dfs(1, 0, 0, 0));
    }
    return 0;
}
posted @   Miraii  阅读(321)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩