[HDOJ - 5282] Senior's String 【DP】

时间:2024-05-01 11:22:56

题目链接:BZOJ - 5282

题目分析

LCS 就是用经典的 O(n^2) DP 解决,f[i][j] 表示 x 串前 i 个字符与 y 串前 j 个字符的 LCS 长度。

f[i][j] = max(f[i - 1][j], f[i][j - 1]);

if (x[i] == y[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);

然后再设置一个状态 g[i][j], 表示 x 串的前 i 个字符中,有多少个长为 f[i][j] 的子序列同时也是 y 串前 j 个字符的子序列。

然后转移的时候,分两种情况:

1) 不包含 x[i] 的子序列, if (f[i - 1][j] == f[i][j]) g[i][j] += g[i - 1][j];

2)包含 x[i] 的子序列,if (f[i - 1][p - 1] + 1 == f[i][j]) g[i][j] += g[i - 1][p - 1]; (p 是 y 串前 j 个字符中最靠后的与 x[i] 相同的位置。)

这样转移就好了。

初始化 g[][] 的时候给 g[0][] 和 g[][0] 赋值。

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm> using namespace std; inline int gmax(int a, int b) {return a > b ? a : b;} const int MaxN = 1000 + 5, Mod = 1000000007; int T, n, m, Lp;
int f[MaxN][MaxN], g[MaxN][MaxN]; char X[MaxN], Y[MaxN]; int main()
{
scanf("%d", &T);
for (int Case = 1; Case <= T; ++Case)
{
scanf("%s%s", X + 1, Y + 1);
n = strlen(X + 1);
m = strlen(Y + 1);
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
f[i][j] = gmax(f[i - 1][j], f[i][j - 1]);
if (X[i] == Y[j]) f[i][j] = gmax(f[i][j], f[i - 1][j - 1] + 1);
}
g[0][0] = 1;
for (int i = 1; i <= n; ++i) g[i][0] = 1;
for (int i = 1; i <= m; ++i) g[0][i] = 1;
for (int i = 1; i <= n; ++i)
{
Lp = 0;
for (int j = 1; j <= m; ++j)
{
if (Y[j] == X[i]) Lp = j;
if (f[i - 1][j] == f[i][j])
{
g[i][j] += g[i - 1][j];
g[i][j] %= Mod;
}
if (Lp != 0 && f[i - 1][Lp - 1] + 1 == f[i][j])
{
g[i][j] += g[i - 1][Lp - 1];
g[i][j] %= Mod;
}
}
}
printf("%d\n", g[n][m]);
}
return 0;
}