很显然,最短长度为两个字符串的长度之和减去最长公共字串的长度。
关键是方案数的统计
首先這個題目肯定是按最長公共子序列的形式進行dp的,因為只有保證消去的一部分是最長公共子序列才能保證最後生成的序列最短。
因此,在記錄方案數的時候我們也按最長公共子序列的生成過程來記錄即可,我們不妨用p[i][j]記錄最長公共子序列的字符數,用f[i][j]表示到第一個字符串第i個位置、第二個字符串第j個位置時生成的序列最短的方案種數。
當a[i]!=b[j]時,p[i][j]=max{p[i-1][j],p[i][j-1]},那麼如果p[i][j]==p[i-1][j],f[i][j]+=f[i-1][j],如果p[i][j]==p[i][j-1],f[i][j]+=f[i][j-1]。如果这次Dp的决策取的是f[i-1][j] 代表最长公共子序列中没有将i进行匹配,最终形成的包含A串1~i和B串1~j的所有字串的最短字符串的个数就等于包含A串1~i-1和B串1~j的所有字串的最短字符串数量,因为这种情况只需要将a[i]放到最后,第二种情况同理。
當a[i]==b[j]時,p[i][j]=p[i-1][j-1]+1,f[i][j]+=f[i-1][j-1]。这种情况只需要将a[i]也就是b[j]选择一个放到该字符串的最后,也是只有一种方案,試想,我們這樣算會不會少算某些部分呢?因為畢竟也可以在a[i]和b[j]不配成一對的情況下生成最短的字符串呀。實際上,是可以證明f[i-1][j-1]包含了上述的情況的。譬如我們假設b[j]和a[i]前面的某個字符配成一對,同時生成了最短的字符串,那麼這個字符串必然是以a[i]結尾的某個最短字符串,而以a[i]結尾的所有最短字符串的個數顯然已經包含在f[i-1][j-1]之中了,因為f[i-1][j-1]本身就表示的是以a[i]為結尾的最短字符串的方案總數。
實際上,這個類似證明求最長公共子序列時如果a[i]==b[j],那麼取p[i][j]=p[i-1][j-1]+1一定是最優的。
注意要使用gets() 同时计数数组应该使用long long 类型
#include<iostream>
#include<cstdio>
#include<cstring>
#define max(a,b) (a>b?a:b)
using namespace std;
long long c[100][100],f[100][100];
char a[100],b[100];
int main()
{
int T,num=0;
scanf("%d",&T); getchar();
while(T--)
{
gets(a+1);gets(b+1);
int n=strlen(a+1),m=strlen(b+1);
memset(c,0,sizeof(c));
memset(f,0,sizeof(f));
for(int i=0;i<=max(n,m);i++) c[i][0]=c[0][i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=(a[i]==b[j])?f[i-1][j-1]+1:max(f[i-1][j],f[i][j-1]);
if(f[i][j]==f[i-1][j]) c[i][j]+=c[i-1][j];
if(f[i][j]==f[i][j-1]) c[i][j]+=c[i][j-1];
if(a[i]==b[j]) c[i][j]=c[i-1][j-1];
}
cout<<"Case #"<<++num<<": "<<m+n-f[n][m]<<" "<<c[n][m]<<endl;
}
while(1);
return 0;
}