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.

Sample Output

4
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[i1][j1]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 }

 

posted @ 2019-03-30 22:40  N_Yokel  阅读(236)  评论(0编辑  收藏  举报