CF682D Alyona和字符串

1 CF682D Alyona和字符串

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

从森林里回来后,\(Alyona\) 开始读一本书。她发现了长度为 \(n\)\(m\) 的两个字符串 \(s\)\(t\)。 和往常一样,有些无聊的 \(Alyona\)决定观察下字符串 \(s\)\(t\),她认为它们非常相似。\(Alyona\) 喜欢不超过 \(10\) 的正整数 \(k\)。她现在想选择字符串 \(s\)\(k\) 个不相交的非空子字符串,这样这些字符串就像字符串t的不相交子字符串一样,并且顺序与字符串s的顺序相同。她还感兴趣的是在所有变化中可能的最大长度。

\(Alyona\) 希望找到满足下列条件的 \(k\) 个非空字符串 \(p_1,p_2,p_3,...,p_k\)

  • \(s\) 可以表示为 \(a_1p_1a_2p_2...a_kp_ka\)\(_k\)\(_+\)\(_1\)\(a_1,a_2,a_3,...,a\)\(_k\)\(_+\)\(_1\) 是可能为空的任意字符串.
  • \(t\) 可以表示为 \(b_1p_1b_2p_2...b_kp_ka\)\(_k\)\(_+\)\(_1\)\(b_1,b_2,b_3,...,b\)\(_k\)\(_+\)\(_1\) 是可能为空的任意字符串.
  • 字符串的长度之和为最大。

3 题解

这道题和 \(LCS\) 十分相似,我们可以考虑使用相似的 \(dp\) 状态。我们先用 \(dp_{i, j}\),表示考虑 \(S\) 串中前 \(i\) 位和 \(T\) 串中前 \(j\) 位得到的最大答案是多少。但是我们发现,由于这道题有 \(k\) 的加入,转移很不方便。

观察到 \(k\) 的数据范围很小,只有 \(10\),我们可以考虑将 \(k\) 作为一维加入状态。此时的 \(dp_{i, j, k}\) 表示考虑 \(S\) 串中前 \(i\) 位和 \(T\) 串中前 \(j\) 位,存在 \(k\) 个字符串时的最大长度。虽然这样看起来可做了许多,但是还存在一个问题:如果 \(S\) 串和 \(T\) 串中的当前位相同,我们也有可能不选用该位。为了将这种情况的转移计算上,我们可以在状态后面再加一维 \(0/1\),表示当前位计入不计入答案。

我们接下来就可以思考转移了。这里一共有 \(2\) 种大情况:当前两位相同或者不相同。

在不相同时,我们显然只能进行一种转移:从 \(dp_{i-1, j, l, 0}, dp_{i, j-1, l, 0}, dp_{i-1, j, l, 1}, dp_{i, j-1, l, 1}\) 四种状态中选取最长的一种状态。这里的意思其实跟 \(LCS\) 中不相同的转移时的意思差不多,唯一需要注意的是:由于我们当前位并不作出贡献,我们只可能从 \(l\) 这一种状态转移过来。

在相同时,我们可以总共有两种大的转移:当前位对答案是否作出贡献。如果不作出贡献,转移跟不相同时一摸一样,这里不再赘述。如果作出贡献,那么我们考虑两种小情况:我们当前的这两位连着之前的一个字符串或者不连着。连着的时候,我们的转移为:\(dp_{i, j, l, 1} = dp_{i-1, j-1, l, 1} + 1\),注意此时由于我们是连着上一个字符串的,所以答案是从 \(l\) 转移过来的。不连着的时候,我们的转移为:\(dp_{i, j, l, 1} = dp_{i-1, j-1, l-1, 0} + 1\)。由于不连着,所以上一个必须不算到答案中去。由于我们这样相当于添加了一个新的字符串,所以要从 \(l-1\) 转移过来。

我们发现,\(dp_{i, j, l, 0} = max(max(dp_{i-1, j, l, 0}, dp_{i, j-1, l, 0}), max(dp_{i-1, j, l, 1}, dp_{i, j-1, l, 1}))\) 这一转移无论 \(s_i\) 是否等于 \(t_j\),都存在。所以我们可以将这个转移单拎出来,使每一次都可以执行这个转移。综上所述,这道题的转移方程为:

\[dp_{i, j, l, 0} = max(max(dp_{i-1, j, l, 0}, dp_{i, j-1, l, 0}), max(dp_{i-1, j, l, 1}, dp_{i, j-1, l, 1})) \\ dp_{i, j, l, 1} = max(dp_{i-1, j-1, l, 1}, dp_{i-1, j-1, l-1, 0}) + 1 \space \space \space \space \space \space \space \space \space \space s_i = t_j \]

注意:最终统计答案时我们要统计的是 \(dp_{n, m, k, 0}\)\(dp_{n, m, k, 1}\) 这两个值中的最大值,这是因为我们无法保证哪种情况是最优的。

4 代码(空格警告):

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1005;
const int K = 15;
int n, m, k;
int dp[N][N][K][3];
char s[N], t[N];
int main()
{
    scanf("%d %d %d", &n, &m, &k);
    scanf("%s %s", s+1, t+1);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            for (int l = 1; l <= k; l++)
            {
                dp[i][j][l][0] = max(max(dp[i-1][j][l][1], dp[i][j-1][l][1]), max(dp[i-1][j][l][0], dp[i][j-1][l][0]));
                if (s[i] == t[j]) dp[i][j][l][1] = max(dp[i-1][j-1][l][1], dp[i-1][j-1][l-1][0])+1;
            }
        }
    }
    cout << max(dp[n][m][k][0], dp[n][m][k][1]);
    return 0;
}

欢迎关注我的微信公众号:智子笔记

posted @ 2021-02-20 10:31  David24  阅读(58)  评论(0编辑  收藏  举报