/*
给出两个串A,B <=2000,构造一个新串,使得该新串的子序列是A,B,且长度最短
求新串的方案数
显然是最小长度n+m-lcs
比赛时,我错误做法是对相邻匹配点中间的字符进行排列,如
abcd
aefd
就固定a,d,中间的b,c,e,f排列,当然需要b在c前,e在f前
但这种做法遇到多个匹配点时就有问题了,如
be
babo
上面的b可以跟下面两个匹配,直接像上面那样子排列会有重复的问题
当时局限在上面的思路,没搞出.
其实不难,直接dp即可
dp[i,j]表示以A[i]或者B[j]结尾的串的个数
如果A[i] = B[j],则dp[i,j] = dp[i-1,j-1] 因为这时必须匹配A[i],B[j]
否则,lcs[i-1,j] > lcs[i,j-1] 则dp[i,j] = dp[i-1,j] 这时新串肯定是A[i]结尾,因为取的是dp[i-1,j]
lcs[i-1,j] < lcs[i,j-1] 则dp[i,j] = dp[i,j-1] 这时新串肯定是B[i-1]结尾
lcs[i-1,j] = lcs[i,j-1] 则dp[i,j] = dp[i-1,j] +dp[i,j-1] 这时新串可以是A[i]或B[j]结尾,但不会重复,因为尾部不同
坑得,当时比赛时,没想打如果取dp[i-1,j],自然是以A[i]结尾的!!!
结尾不同,不会导致重复计数的!!
用组合数学的方法算时,不清楚尾部是什么
*/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#include<set>
#include<cstring>
#include<string>
#include<stack>
#include<cassert>
#include<sstream>
#include<list>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 2010;
const int MOD = 1000000007;
int dp[MAXN][MAXN], lcs[MAXN][MAXN];
char A[MAXN], B[MAXN];
int main()
{
//freopen("in","r", stdin);
//freopen("out","w", stdout);
while (~scanf("%s%s", A+1, B+1)) {
int n = strlen(A+1);
int m = strlen(B+1);
memset(dp, 0, sizeof dp);
memset(lcs, 0, sizeof lcs);
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for (int i = 0; i <= m; i++) {
dp[0][i] = 1;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m;j ++) {
if (A[i] == B[j]) {
lcs[i][j] = lcs[i-1][j-1]+1;
dp[i][j] = dp[i-1][j-1];
} else if (lcs[i-1][j] > lcs[i][j-1]) {
lcs[i][j] = lcs[i-1][j];
dp[i][j] = dp[i-1][j];
} else if (lcs[i-1][j] < lcs[i][j-1]) {
lcs[i][j] = lcs[i][j-1];
dp[i][j] = dp[i][j-1];
} else {
lcs[i][j] = lcs[i-1][j];
dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % MOD;
}
}
//cout<<lcs[n][m]<<endl;
cout<<dp[n][m]<<endl;
}
return 0;
}
|
|
常用链接
随笔分类
Links
搜索
最新评论
|
|