[bzoj2423][HAOI2010]最长公共子序列

来自FallDream的博客,未经允许,请勿转载,谢谢。

--------------------------------------------------------------------------------------------------------

给定两个长度为n,m字符串,求最长公共子序列长度和方案数$\mod 10^{8}$   $n,m\leqslant 5000$

题解:第一问相信大家都会...f[i][j]表示第一个字串前i个,第二个字串前j个最长的长度$f[i][j]=\begin{equation}
 \left\{
   \begin{aligned}
   \max(f[i][j-1],f[i-1][j]) \\
      f[i-1][j-1]+1(a[i]=b[j]) \\
   \end{aligned}
  \right.
\end{equation}$

然后第二个问,我们考虑f[i][j]=k,那么就从加上f[i-1][j]和f[i][j-1]中=k的方案数。

如果a[i]=b[j],那么还要算上f[i-1][j-1]的方案数;但是如果a[i]!=b[j]且f[i-1][j-1]=k,就要减去它的方案数了。

#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 100000000
#define ll long long
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int f[2][5005],r[2][5005];
char s1[5005],s2[5005];
int n,m;

int main()
{    
    scanf("%s",s1+1);n=strlen(s1+1)-1;
    scanf("%s",s2+1);m=strlen(s2+1)-1;
    int now=1,pre=0;
    for(int k=0;k<=m;k++)r[0][k]=1;r[1][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[now][j]=max(f[pre][j],f[now][j-1]);r[now][j]=0;
            if(s1[i]==s2[j])f[now][j]=max(f[now][j],f[pre][j-1]+1); 
            if(s1[i]==s2[j]&&f[now][j]==f[pre][j-1]+1) r[now][j]+=r[pre][j-1];
            if(f[pre][j]==f[now][j]) r[now][j]+=r[pre][j];
            if(f[now][j-1]==f[now][j]) r[now][j]+=r[now][j-1];
            if(f[pre][j-1]==f[now][j]) r[now][j]-=r[pre][j-1];
            r[now][j]=(r[now][j]+mod)%mod;
        }
        now=pre;pre=1-pre;    
    }
    printf("%d\n%d",f[pre][m],r[pre][m]);
    return 0;
}

 

posted @ 2017-03-27 10:41  FallDream  阅读(630)  评论(0编辑  收藏  举报