题意翻译
【题目描述】
输入两个A~Z组成的字符串(长度均不超过30),找一个最短的串,使得输入的两个串均是它的子序列(不一定连续出现)。你的程序还应统计长度最短的串的个数。
e.g.:ABAAXGF和AABXFGA的最优解之一为AABAAXGFGA,一共有9个解。
【输入格式】
有多组数据。第一行一个整数T表示数据组数。接下来的2T行,每行一个字符串,含义如题所示。
【输出格式】
共T行。第i行格式为
Case #i: x y
其中x为最短串的长度,y为最优解的个数。
题目描述
解析:
作为一名生物渣渣,蒟蒻再次感到了多方面综合发展的必要性。。。。
最近老做最短路,今天我们就来做一道很水(nan)的动态规划~
下面我们进入正题,这个题的本质是求:最短公共超序列的长度和个数;
所谓的最短公共超序列,其实可以这样通俗地理解:找到一个最短的串,让给定的两个串都是这个新串的子序列。
这个题和最短公共超序列板子的唯一的不同就是需要统计超序列的个数。
于是我们可以先跑一边最短公共超序列。
设dp[i][j]表示a串前i个字符,b串前j个字符所构成的最短公共超序列长度。
在真正进行dp之前,首先初始化dp数组。
for(int i=1;i<=100;i++)dp[i][0]=dp[0][i]=i;
然后我们来想状态转移方程。
当a[i]==b[j]时,最短公共超序列最后一位是确定的,它的长度一定是这两个串之前的超序列长度+1,其中的1表示这个字符本身。
当他们不同时,去选择之前状态中最短的一个,实际上是对应超序列中最后一位a[i]还是b[j]。
于是完整的状态转移方程不难写出:
if(a[i-1]==b[j-1])dp[i][j]=dp[i-1][j-1]+1;//a[i],b[j]减1是因为循环变量是从1循环到<=len;
else dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1);
跑完了最短公共超序列,我们就可以开始统计它的个数。
设f[i][j]表示a串前i个字符,b串前j个字符所构成的最短公共超序列个数。
同样,在正式统计之前,先进行初始化。
for(int i=0;i<=100;i++)f[i][0]=f[0][i]=1;
然后我们开始统计。
当a[i]==b[j]的时候,序列的末尾是确定的,只有一种可能性。所以f[i][j]=f[i-1][j-1];
当a[i]!=b[j]的时候,哪个超序列更短取哪个。
最后就是当他们的超序列不同但是长度相同时,两个都可以取,加起来就好。
完整的统计过程:
if(a[i-1]==b[j-1])f[i][j]=f[i-1][j-1];
else if(dp[i-1][j]<dp[i][j-1])f[i][j]=f[i-1][j];
else if(dp[i-1][j]>dp[i][j-1])f[i][j]=f[i][j-1];
else f[i][j]=f[i-1][j]+f[i][j-1];
最后上AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
using namespace std;
string a,b;
int dp[105][105],f[105][105],t;
int main()
{
cin>>t;
getchar();
for(int k=1;k<=t;k++)
{
memset(dp,0,sizeof(dp));
memset(f,0,sizeof(f));
getline(cin,a);
getline(cin,b);
int len1=a.size(),len2=b.size();
for(int i=1;i<=100;i++)dp[i][0]=dp[0][i]=i;
for(int i=1;i<=len1;i++)
{
for(int j=1;j<=len2;j++)
{
if(a[i-1]==b[j-1])dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1);
}
}
for(int i=0;i<=100;i++)f[i][0]=f[0][i]=1;
for(int i=1;i<=len1;i++)
{
for(int j=1;j<=len2;j++)
{
if(a[i-1]==b[j-1])f[i][j]=f[i-1][j-1];
else if(dp[i-1][j]<dp[i][j-1])f[i][j]=f[i-1][j];
else if(dp[i-1][j]>dp[i][j-1])f[i][j]=f[i][j-1];
else f[i][j]=f[i-1][j]+f[i][j-1];
}
}
printf("Case #%d: %d %d\n",k,dp[len1][len2],f[len1][len2]);
}
return 0;
}
到此为止,这个题已经讲解完了,接下来是笔者跟这道题的渊源。
我最开始接触OI是在半年前,也许有人会惊讶我半年就学到了SCS这种比较难一些的算法,我之所以学的比同年纪的人快,
很大程度上是和这道题有关系的。
我第一次看到这道题,是4个月前,是因为同社团的人为了出风头,在网上随便找了篇题解贴到了我们OJ上,结果测评过了,
当然,这位同学之后被老师发现了,于是就被骂了一顿。。。。
当时我就想,如果我能凭我的能力做出来这道题,该多么的好,于是我就开始奋斗,
不夸张地说,真是“夜以继日”地学;
在同社团的人也算是比较优秀了,到了更高一级的班学习。
当然,我也觉得信息学十分有乐趣,学到现在,我也不光只是想做出来这道“电子人的基因”了,还有更多,更富有挑战性,更有趣的题等着我去做。
但我可以说,如果没有这道题给我的激励作用,我不可能学的比现在更好。真要说起来,我还得感谢那位抄代码的同学。
终于, 2019-04-22 20:58:55;我做出了这道日思夜想的“电子人的基因”。
这就是一道“电子人的基因”的力量。