【JZOJ 3747】Problem C

本文介绍了一种求解两个基因串之间最长公共子序列(LCS)问题的有效算法,并通过动态规划的方法解决了如何计算不同长度基因串的匹配数量。文章提供了一个具体的实现案例,详细解释了状态压缩技巧及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

基因串是由ACGT4个字母组成的,我们有一个长度为n的基因串S。想要知道长度为m的基因串中,与S的最长公共子序列分别为0; 1; ; n的串各有几个。

输出答案关于10^9 + 7的余数。

n<=10;m<=1000

Solution

先看一下暴力,
每次枚举出一个大串b以后,用DP来算:
fi,j表示大串做到位置i,小串匹配到j的最大匹配,
转移:

fi,j=max(fi1,j,fi,j1,fi1,j1+[aj=bi])

仔细观察,发现对于相同的i,如对于每一个j,fi,j都相同,那么这两个fi对后面的影响都是一样的,哪怕b不一样;
还有性质,对于固定的i,fi,j是不下降的,且上升最多+1,所以可以把它差分以后压成一个01串;
Fi,S表示b串枚举到i位,S为压缩的fi,j的状态,值表示方案数,
每次转移枚举这位选什么,可以计算出当前位选了这个数后的新的S,然后更新;

细节蛮多的,读者自己想一下吧

复杂度:O(m2n4)

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int N=1005,M=12,mo=1e9+7;
int m,n;
int a[N],g[M];
LL f[N][2125];
LL ans[N];
int er[M+1];
int f1[2050][4],f2[2050];
int main()
{
    int q,w;
    er[1]=1;fo(i,2,12)er[i]=er[i-1]<<1;
    char ch=' ';
    for(;ch!='A'&&ch!='C'&&ch!='G'&&ch!='T';ch=getchar());
    for(;ch=='A'||ch=='C'||ch=='G'||ch=='T';ch=getchar())
    {
        if(ch=='A')a[++n]=1;
        if(ch=='C')a[++n]=2;
        if(ch=='G')a[++n]=3;
        if(ch=='T')a[++n]=4;
    }
    fo(i,0,er[n+1]-1)
        fo(j,1,4)
        {
            fo(k,1,n)g[k]=g[k-1]+((er[k]&i)?1:0);
            f2[i]=g[n];
            q=0;
            fod(k,n,1)g[k]=max(g[k],g[k-1]+(j==a[k]));
            fo(k,1,n)g[k]=max(g[k],g[k-1]);
            q=0;
            fo(k,1,n)if(g[k]!=g[k-1])q+=er[k];
            f1[i][j]=q;
        }
    scanf("%d",&m);
    f[0][0]=1;
    fo(I,0,m-1)
        fo(i,0,er[n+1]-1)if(f[I][i])
        {
            fo(j,1,4)
            {
                q=f1[i][j];
                f[I+1][q]=(f[I+1][q]+f[I][i])%mo;
            }
        }
    fo(i,0,er[n+1]-1)ans[f2[i]]=(ans[f2[i]]+f[m][i])%mo;
    fo(i,0,n)printf("%lld\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值