LCS及方案数(DP)

Description

对于一个序列𝑎[1], 𝑎[2], … , 𝑎[𝑛],其子序列为一序列𝑝[1], 𝑝[2], … , 𝑝[𝑚],满 足1 ≤ 𝑝[1] < 𝑝[2] < ⋯ < 𝑝[𝑚] ≤ 𝑛, 1 ≤ 𝑚 ≤ 𝑛。𝑎的两个子序列𝑥, 𝑦是不同的,当 且其长度不同,或存在一个位置𝑖,满足𝑥[𝑖] ≠ 𝑦[𝑖]。

给定两个长度分别为𝑛, 𝑚的序列𝐴, 𝐵,定义序列二元组(𝑢, 𝑣)是好的,当且 仅当满足

  1. 𝑢为𝐴的子序列且𝑣为𝐵的子序列

  2. 𝑢, 𝑣长度相同

  3. 对于所有的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;
}
posted @ 2017-10-23 08:38  void_f  阅读(807)  评论(0编辑  收藏  举报