时间限制 \(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\):
这道题和 \(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_{n, m, k, 0}\) 和 \(dp_{n, m, k, 1}\) 这两个值中的最大值,这是因为我们无法保证哪种情况是最优的。
#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;
}
原文:https://www.cnblogs.com/david24/p/14419547.html