【DP 训练】Cyborg Genes, UVa 10723

时间:2021-10-10 19:08:48

题意:给出两个串a,b,去构建另一个串,新构建出来的串要满足两个性质。一,在这个新的串中选出一个子集是a串,另外选出一个子串是b,满足这个条件后,要求这个串的长度最短。也可以这样说,ab两个串合并为一个新串,不改变a,b串本身的相对位置,但是要求新串长度最短。输出这样的新串的个数

所以基本上可以想到,是和LCS有关的,再细想,那就是算出LCS的长度,然后ab两串长度之和减去LCS的长度,相当于ab两串合并为一串,消去他们的共有元素,那么新串中还是包含了a,b串的所有元素还是可以选出一部分子集来得到a或b串的。

但问题时,怎么构建,另外有多少种构建方法,实际上构建的方案数就是最后新串的个数

我们可以模仿LCS的构建方法来思考这个问题    c[i][j]表示a串前i个元素和b串前j个元素所能得到的方案数。l[i][j]表示LCS的长度

若a[i]=b[j],那么c[i][j]=c[i-1][j-1],即a串前i-1个元素和b串前j-1个元素得到的组合串的末尾加上一个相同的元素a[i],那么得到的新的组合串的个数还是和之前的组合串的个数一样

若a[i]!=b[j],  l[i][j]=max { l[i-1][j] , l[i][j-1]}

若l[i-1][j]>l[i][j-1],那说明从l[i-1][j]这种状态开始构建才能得到最终的LCS同时最终的组合串才不能漏掉共有的元素,所以c[i][i]=c[i-1][j],即在a串i-1个元素和b串j个元素组成的组合串的后面加上a[i],那么得到的新的组合串的个数和之前的组合串的个数是相同的

若l[i][j-1]>l[i-1][j],道理和上面是一样的,所以c[i][j]=c[i][j-1],相当于在之前的组合串后面加上元素b[j],得到新的组合串的个数不变

若l[i][j-1]=l[i-1][j],说明从两种状态都是能得到最终的LCS并且最终的组合串不会漏掉任何相同的公共元素,所以c[i][j]=c[i-1][j]+c[i][j-1] , 即用a串的i-1个元素和b串的j个元素组成的组合串的最后加上a[i]得到新的组合串和之前的组合串个数相同,另外用a串的i个元素和b串的的j-1个元素组成的组合串的最后加上b[j]得到新的组合串和之前的组合串个数相同,那么就是两者之和

 


#include<bits/stdc++.h>
using namespace std;
int T;char s1[40],s2[40];int len1,len2;
int dp[40][40];
long long c[40][40];
int main(void)
{
	scanf("%d",&T);getchar();
	for(int i=1;i<=T;i++)
	{
		gets(s1+1);gets(s2+1);
		memset(dp,0,sizeof(dp));
		memset(c,0,sizeof(c));
		len1 = strlen(s1+1),len2 = strlen(s2+1);
		//注意c数组的边界条件(从0开始..) 
		for(int i=0;i<=len1;i++)c[i][0]=1;
		for(int i=0;i<=len2;i++)c[0][i]=1;
		for(int i=1;i<=len1;i++)
		{
			for(int j=1;j<=len2;j++)
			{
				if(s1[i]==s2[j])
				{
					dp[i][j] = dp[i-1][j-1]+1;
					c[i][j] = c[i-1][j-1];
				}
				else
				{
					if(dp[i-1][j]>dp[i][j-1])
					{
						dp[i][j] = dp[i-1][j];
						c[i][j] = c[i-1][j];
					}
					else if(dp[i][j-1]>dp[i-1][j])
					{
						dp[i][j] = dp[i][j-1];
						c[i][j] = c[i][j-1];
					}
					else
					{
						dp[i][j] = dp[i-1][j];
						c[i][j] = c[i-1][j]+c[i][j-1];
					}
				}
			}			
		}
		printf("Case #%d: %d %lld\n",i,len1+len2-dp[len1][len2],c[len1][len2]);
	}
	return 0;
}