Description
对于一个序列?[1], ?[2], … , ?[?],其子序列为一序列?[1], ?[2], … , ?[?],满 足1 ≤ ?[1] < ?[2] < ⋯ < ?[?] ≤ ?, 1 ≤ ? ≤ ?。?的两个子序列?, ?是不同的,当 且其长度不同,或存在一个位置?,满足?[?] ≠ ?[?]。
给定两个长度分别为?, ?的序列?, ?,定义序列二元组(?, ?)是好的,当且 仅当满足
?为?的子序列且?为?的子序列
?, ?长度相同
对于所有的1 ≤ ? ≤ ?的长度,满足?[?[?]] = ?[?[?]]
两个二元组(?, ?), (?, ?)是不同的,当且仅当? ≠ ?或? ≠ ?。 现在希望你求出最大的?,满足存在二元组(?, ?)使得?的长度为?且(?, ?)是 好的。并求出满足长度为?的好的二元组的对数。答案对1e9+ + 7取模。
Input Format
包含两行字符串,分别表示序列?, ?。
Output Format
包含两行。 第一行为?。 第二个行为合法的二元组的对数对1e9 + 7取模的结果
Hint
对应100%的数据,?, ? ≤ 5000,保证序列只包含小写字母。
Solution
那么显然,这是一道动规题,在LCS的基础加上了求方案数
考试的时候只想出了求LCS,后面的方案数就完。。。全没想出来
关于LCS就一个数组f[i][j]表示A串前i个,B串前j个的LCS,
那么f[i][j]=max(f[i-1][j],f[i][j-1]),且当A[i]==B[i]时,f[i][j]=max(f[i-1][j-1]+1)
那么怎么求方案数呢?
首先设一个数组g[i][j]表示对于A串前i个,B串前j个的LCS的方案数,
那么就有,
f[i][j] = f[i - 1][j];
g[i][j] = g[i - 1][j];
if (f[i][j - 1] == f[i][j])
g[i][j] += g[i][j - 1];
else if (f[i][j - 1] > f[i][j]) {
f[i][j] = f[i][j - 1];
g[i][j] = g[i][j - 1];
}
if (A[i - 1] == B[j - 1])
if (f[i - 1][j - 1] + 1 > f[i][j]) {
f[i][j] = f[i - 1][j - 1] + 1;
g[i][j] = g[i - 1][j - 1];
} else if (f[i - 1][j - 1] + 1 == f[i][j])
g[i][j] += g[i - 1][j - 1];
其实也不难想到这样的转移,但只是这样会出问题,
比如A=“abcd",B="abef",g[4][4]会等于g[3][4]+g[4][3]=2,这显然是错误的,
我们发现,g[3][4]=g[3][3]+g[2][4],g[4][3]=g[3][3]+g[4][2],我们这里的g[3][3]是不必算两次,
也就是说这里的g[3][3]重复计算了一次,
但并不是每个转移都是这样,条件如下,
if (f[i - 1][j - 1] == f[i][j] && f[i - 1][j] == f[i][j - 1])
g[i][j] -= g[i - 1][j - 1];
不再多说了,
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 5012
using namespace std;
const int yh = 1e9 + 7;
int n, m, f[N][N];
long long g[N][N];
char A[N], B[N];
int main()
{
scanf("%s\n%s", A, B);
n = strlen(A), m = strlen(B);
for (int i = 0; i <= n; ++i) g[i][0] = 1;
for (int i = 0; i <= m; ++i) g[0][i] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
f[i][j] = f[i - 1][j];
g[i][j] = g[i - 1][j];
if (f[i][j - 1] == f[i][j])
g[i][j] = (g[i][j] + g[i][j - 1]) % yh;
else if (f[i][j - 1] > f[i][j]) {
f[i][j] = f[i][j - 1];
g[i][j] = g[i][j - 1];
}
if (A[i - 1] == B[j - 1]) {
if (f[i - 1][j - 1] + 1 > f[i][j]) {
f[i][j] = f[i - 1][j - 1] + 1;
g[i][j] = g[i - 1][j - 1];
} else if (f[i - 1][j - 1] + 1 == f[i][j])
g[i][j] += g[i - 1][j - 1] % yh;
}
if (f[i - 1][j - 1] == f[i][j] && f[i - 1][j] == f[i][j - 1])
g[i][j] -= g[i - 1][j - 1];
}
printf("%d\n", f[n][m]);
printf("%d\n", (g[n][m] % yh + yh) % yh);
return 0;
}