BZOJ 2423 (求LCS的长度和种类数)
Description
字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij = yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。
Input
第1行为第1个字符序列,都是大写字母组成,以”.”结束。长度小于5000。
第2行为第2个字符序列,都是大写字母组成,以”.”结束,长度小于5000。
Output
第1行输出上述两个最长公共子序列的长度。
第2行输出所有可能出现的最长公共子序列个数,答案可能很大,只要将答案对100,000,000求余即可。
Sample Input
ABCBDAB.
BACBBD.
BACBBD.
Sample Output
4
7
7
反思:题目虽然看题解看懂了,自己也敲出来了,但是当别人问的时候不能清晰的讲出自己思路(甚至还讲错了,这个是真TM尴尬),这样是失败的,虽然AC了,然并卵;
这题第2问严格的来说并不能算动态规划(不满足无后效性),只是借用其中状态如何转移;
题解:第一问模板题,第二问不会写
有2个问题:第LCS的个数,代码会爆空间(需要滚动数组)
f[i][j]表示A前i位,B前j位的最长公共子序列长度,用g[i][j]表示A前i位,B前j位的最长公共子序列数目
g[i][j]如何转移呢?考虑这是从那一步推过来的,
比如当 f[i][j]=f[i−1][j],就可以认为从f[i-1][j]转移过来,
那么g[i][j]=g[i-1][j];
那么有如下关系式:
当f[i][j]=f[i−1][j],g[i][j]+=g[i−1][j]
当f[i][j]=f[i][j−1],g[i][j]+=g[i][j−1]
当a[i]=b[j]且f[i][j]=f[i−1][j−1]+1,g[i][j]+=g[i−1][j−1],看起来好像没啥问题,but...样例都没法过,Orz
其实是忽略了一种情况(本质上是对这个状态转移不是特别清晰),
当a[i]≠b[j],并且f[i][j]=f[i−1][j−1]f[i][j]=f[i−1][j−1],(就是a,b的最后一位均没有匹配,会导致上面的2个if条件都会满足,
g[i][j]同时累计上g[i-1][j]和g[i][j-1]时,明显有重复的情况 。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 const int mod=100000000; 7 const int maxn=5005; 8 char a[maxn], b[maxn]; 9 int f[2][maxn],g[2][maxn]; 10 11 int main() 12 { 13 //freopen("in.txt", "r", stdin); 14 15 scanf("%s",a+1);scanf("%s",b+1); 16 int n=strlen(a+1)-1, m=strlen(b+1)-1; 17 18 g[1][0]=1; 19 for(int j=0;j<=m;j++) 20 g[0][j]=1; 21 22 for(int i=1;i<=n;i++) 23 { 24 int now=i&1,pre=now^1; 25 for(int j=1; j<=m; j++) 26 { 27 f[now][j]=max(f[pre][j], f[now][j-1]); 28 if(a[i]==b[j]) 29 { 30 f[now][j]=max(f[now][j],f[pre][j-1]+1); 31 if(f[now][j]==f[pre][j-1]+1) 32 g[now][j]=g[pre][j-1]; 33 } 34 else 35 { 36 g[now][j]=0; 37 if(f[now][j]==f[pre][j-1]) 38 g[now][j]-=g[pre][j-1]; 39 } 40 if(f[now][j]==f[pre][j]) 41 g[now][j]=(g[now][j]+g[pre][j])%mod; 42 43 if(f[now][j]==f[now][j-1]) 44 g[now][j]=(g[now][j]+g[now][j-1])%mod; 45 } 46 } 47 printf("%d\n%d",f[n&1][m],g[n&1][m]); 48 return 0; 49 }