HAOI2010 最长公共子序列

Posted fengxunling

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HAOI2010 最长公共子序列相关的知识,希望对你有一定的参考价值。

题目链接:戳我

30分暴力。。。。暴力提取子序列即可qwqwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define MAXN 5010
using namespace std;
int lena,lenb,n,ans,cnt;
int dp[MAXN][MAXN];
char a[MAXN],b[MAXN],cur[MAXN],pre[MAXN];
map<string,int>sum;
inline void init(int pos)
{
    if(cnt==n)
    {
        string s;
        for(int i=1;i<=cnt;i++) s+=pre[i];
        sum[s]++;
        return;
    }
    if(pos>=lenb) return;
    for(int i=pos+1;i<=lenb;i++)
    {
        pre[++cnt]=b[i];
        init(i);
        cnt--;
    }
}
inline void solve(int pos)
{
    if(cnt==n)
    {
        string s;
        for(int i=1;i<=cnt;i++) s+=cur[i];
        ans+=sum[s];
        return;
    }
    if(pos>=lena) return;
    for(int i=pos+1;i<=lena;i++)
    {
        cur[++cnt]=a[i];
        solve(i);
        cnt--;
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    freopen("ce.out","w",stdout);
    #endif
    scanf("%s%s",a+1,b+1);
    lena=strlen(a+1)-1;
    lenb=strlen(b+1)-1;
    for(int i=1;i<=lena;i++)
    {
        for(int j=1;j<=lenb;j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
        }
    }
    n=dp[lena][lenb];
    init(0);
    cnt=0;
    solve(0);
    printf("%d
%d
",n,ans);
    return 0;
}

考虑满分算法?题解的话强烈安利Flash_hu dalao的题解

第一问很好做,也就是f[i][j]表示第一个序列到第i位,第二个序列到第j位,最长的公共子序列的长度。f[i][j]可以从f[i-1][j]和f[i][j-1]转移过来,如果a[i]==b[j]的话还可以从f[i-1][j-1]+1转移过来。

对于第二问,我们设sum[i][j]表示第一个序列到第i位,第二个序列到第j位,最长公共子序列长度为f[i][j]的子序列个数。显然如果f[i-1][j]==f[i][j]的话,我们是可以从sum[i-1][j]转移过来的,f[i][j-1]==f[i][j]同理。额外的,如果f[i-1][j-1]+1==f[i][j]时,我们还可以累加sum[i-1][j-1]的答案。

就这样就结束了吗?不对,你会发现W掉了。为什么呢?

这是因为如果当f[i-1][j-1]==f[i][j]的时候,sum[i-1][j-1]的值对sum[i-1][j]和sum[i][j-1]各贡献了一次。而我们累加到sum[i][j]的时候相当于重算了一次,所以还要记得减去哦qwqwq

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 5010
#define mod 100000000
using namespace std;
int lena,lenb;
int f[2][MAXN],sum[2][MAXN];
char a[MAXN],b[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    freopen("ce.out","w",stdout);
    #endif
    scanf("%s%s",a+1,b+1);
    lena=strlen(a+1)-1,lenb=strlen(b+1)-1;
    for(int i=0;i<=lenb;i++) sum[0][i]=1;
    sum[1][0]=1;
    for(int i=1;i<=lena;i++)
    {
        for(int j=1;j<=lenb;j++)
        {
            sum[1][j]=0;
            f[1][j]=max(f[1][j-1],f[0][j]);
            if(a[i]==b[j]) f[1][j]=max(f[1][j],f[0][j-1]+1);
            if(a[i]==b[j]&&f[0][j-1]+1==f[1][j]) sum[1][j]+=(sum[1][j]+sum[0][j-1])%mod;
            if(f[1][j-1]==f[1][j]) sum[1][j]=(sum[1][j]+sum[1][j-1])%mod;
            if(f[0][j]==f[1][j]) sum[1][j]=(sum[1][j]+sum[0][j])%mod;
            if(a[i]!=b[j]&&f[0][j-1]==f[1][j]) sum[1][j]=(sum[1][j]+mod-sum[0][j-1])%mod;
        }
        swap(f[0],f[1]),swap(sum[0],sum[1]);
    }
    printf("%d
%d
",f[0][lenb],sum[0][lenb]);
    return 0;
}

以上是关于HAOI2010 最长公共子序列的主要内容,如果未能解决你的问题,请参考以下文章

HAOI2010 最长公共子序列

P2516 [HAOI2010]最长公共子序列 题解(LCS)

bzoj 2423: [HAOI2010]最长公共子序列dp+计数

COGS 445. [HAOI2010]最长公共子序列

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

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