BZOJ3864 Hero meet devil

Description

给出一个字符串S,这个字符串只由,ACGT四个字母组成。对于每个1一|S|中的每一个i.求出满足以下条件的字符串T的个数:

  • 长度为m 
  • 只由ACGT组成
  • lcs为i 

Solution

第一步考虑已知两个串,计算两串之间的lcs,i表示A串指针,j表示B串指针

$$f[i][j]=max(f[i-1][j],f[i][j-1])$$

$$if(s[i]==t[j]) f[i][j]=max(f[i-1][j-1]+1)$$

由于s串比较短,把f数组用差分压缩成二进制状态 第二步是外层DP,$dp[i][j]$表示,已经生成了i位的字符串,此时lcs为j的字符串个数;$tr[j][k]$表示j状态下增加字符k得到的状态

$$dp[i+1][tr[j][k]]+=dp[i][j]$$

DP套DP求解

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int T,n,m,a[20],tran[100000][4],f[2][20],dp[1005][40000],ans[20];
const int mod=1000000007;
char s[20];
inline int read()
{
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+ch-'0';
        ch=getchar();
    }
    return f*w;
}
int change(char x)
{
    if(x=='A')
        return 1;
    if(x=='C')
        return 2;
    if(x=='G')
        return 3;
    return 4;
}
int trans(int v,int c)
{
    memset(f,0,sizeof(f));
    for(int i=0;i<n;i++)
        f[0][i+1]=f[0][i]+((v>>i)&1);
    for(int i=1;i<=n;i++)
    {
        f[1][i]=max(f[0][i],f[1][i-1]);
        if(a[i]==c)
            f[1][i]=max(f[1][i],f[0][i-1]+1);
    }
    int ret=0;
    for(int i=0;i<n;i++)
        if(f[1][i+1]-f[1][i])
            ret|=(1<<i);
    return ret;
}
int cnt(int x)
{
    int ret=0;
    while(x)
    {
        if(x&1)
            ret++;
        x>>=1;
    }
    return ret;
}
int main()
{
    T=read();
    for(;T;T--)
    {
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        scanf("%s",s+1);
        m=read();
        n=strlen(s+1);
        for(int i=1;i<=n;i++)
            a[i]=change(s[i]);    
        for(int i=0;i<(1<<n);i++)
            for(int j=1;j<=4;j++)
                tran[i][j]=trans(i,j);
        dp[0][0]=1;
        for(int i=1;i<=m;i++)
            for(int j=0;j<(1<<n);j++)
                for(int k=1;k<=4;k++)
                {
                    int temp=tran[j][k];
                    (dp[i][temp]+=dp[i-1][j])%=mod;
                }
        for(int i=0;i<(1<<n);i++)
            (ans[cnt(i)]+=dp[m][i])%=mod;
        for(int i=0;i<=n;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}
Hero meet devil

 

posted @ 2020-07-28 21:51  QDK_Storm  阅读(204)  评论(0编辑  收藏  举报