[解题记录] NOIP2015 提高组 子串

NOIP2015 提高组 子串#

题意简述#


有两个仅包含小写英文字母的字符串 AB

现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 B 相等?

注意:子串取出的位置不同也认为是不同的方案。

解题思路#


为了保证无后效性,用 i 表示到了 A 的第一个字符,联想到 NOI Online 2022 T3 字符串,用 j 表示成功匹配了几个字符,用 k 表示使用了几个字串,但是发现这样不能轻松的转移,于是我们再加上一维 l,表示第 i 位是否选择

可以得到状态转移方程:

  1. 如果 ai=bj

    fi,j,k,0=fi1,j,k,0+fi1,j,k,1fi,j,k,1=fi1,j1,k,1+fi1,j1,k1,0+fi1,j1,k1,1

    分别表示不选和选,其中选又有三种情况,如果 i1 选了,则可以新开一个或者沿用之前的字串,如果 i1 没有选,那么只能新开一个

  2. 否则,

    fi,j,k,1=0,不能选还要强行选,那么方案数归零

    fi,j,k,0=fi1,j,k,1+fi1,j,k,0

空间复杂度是 O(nmk),会爆空间,但是第一位可以用滚动数组优化

初始化为 f0,0,0,0=f1,0,0,0=1,因为后面三个为零的清况不会被更新到,但是又能算上一个方案

Code#


#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10, M = 2e2 + 10, mod = 1e9 + 7;
int f[2][M][M][2], n, m, c;
char a[N], b[M];
int main() {
    scanf("%d%d%d", &n, &m, &c);
    scanf("%s%s", a + 1, b + 1);
    f[0][0][0][0] = f[1][0][0][0] = 1;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            for (int k = 1; k <= c; ++k) {
                if (a[i] == b[j]) {
                    f[i & 1][j][k][0] = (f[(i - 1) & 1][j][k][0] + f[(i - 1) & 1][j][k][1]) % mod;
                    f[i & 1][j][k][1] = (f[(i - 1) & 1][j - 1][k][1] + (f[(i - 1) & 1][j - 1][k - 1][0] + f[(i - 1) & 1][j - 1][k - 1][1]) % mod) % mod;
                }
                else {
                    f[i & 1][j][k][1] = 0;
                    f[i & 1][j][k][0] = (f[(i - 1) & 1][j][k][1] + f[(i - 1) & 1][j][k][0]) % mod;
                }
            }
    printf("%d\n", (f[n & 1][m][c][1] + f[n & 1][m][c][0]) % mod);
    return 0;
}
posted @   Miraii  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩