BZOJ3864 Hero meet devil dp套dp

还是来存个dp套dp模板吧

题目传送门

题意是给你一个由ATGC组成的字符串,长度n<=15,求长度为m(<=1000)的字符串里有多少个满足与原串的最长公共子序列长度为i,i取0到n所有值

考虑暴力,枚举所有长为m的字符串,dp求LCS

不说了时间复杂度绝对爆炸

来考虑递推式 :dp[i][j]=max(max(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]+[a[i]==b[j]](相等为1不等为0))

那么,所有的dp[i]中,相邻的j差最多为1

那么,我们可以把它压成状态,dp[i][sta]表示a在第i位,b的状态为sta时的方案数

至于怎么转移这个问题,肯定不能每次枚举对吧

那么就只好预处理出来了,用nxt[sta][j]表示状态为sta,下一步为k时转移到的状态

最后统计状态sta中有多少个1,因为我们存的是差分结果,所以累加起来就是最后一位时的最长公共子序列长度(可以回忆最初的递推式,最初时的dp[n][m]就是LCS长度,对吧)(__builtin_popcount(i)是输出i转成2进制由多少个1),枚举一遍状态,统计入对应答案,然后就可以输出了

代码一如既往地难看

#include<bits/stdc++.h>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iostream>
#define ll int
#define re register
#define inf 0x3f3f3f3f
#define inl inline
#define sqr(x) (x*x)
//#define eps 1e-8
#define debug printf("debug
");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
using namespace std;
const ll mod=1e9+7;
const ll MSTA=1<<15;
const ll MAXN=20;
inl ll read() {
    re ll x = 0; re int f = 1;
    char ch = getchar();
    while(ch<'0'||ch>'9') { if(ch== '-' ) f = -1; ch = getchar(); }
    while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x * f;
}
inl char readc() {
    char ch=getchar();
    while(('z'<ch||ch<'a')&&('Z'<ch||ch<'A')) ch=getchar();
    return ch;
}
inl void write(re ll x){
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inl void writeln(re ll x){
    if(x<0) {x=-x;putchar('-');}
    write(x); puts("");
}
inl ll gcd(re ll x,re ll y){while(y^=x^=y^=x%=y);return x;}
inl void FR() {
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
}
inl void FC() {
    fclose(stdin);
    fclose(stdout);
}
char ss[MAXN],dna[10]={'A','T','G','C'};
ll m,n,nxt[MSTA][10],dp[5][MSTA],ans[MAXN];
void init() {
    ll f[MAXN],g[MAXN];
    for(re ll i=0;i<(1<<n);i++) {
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        for(re ll j=1;j<=n;j++) f[j]=f[j-1]+((i>>(j-1))&1);
        for(re ll k=0;k<4;k++) {
            for(re ll j=1;j<=n;j++) {
                g[j]=max(g[j-1],f[j]);
                if(dna[k]==ss[j]) g[j]=max(g[j],f[j-1]+1);
            }
            nxt[i][k]=0;
            for(re ll j=0;j<n;j++) {
                if(g[j+1]-g[j]) {
                    nxt[i][k]|=(1<<j);
                }
            }
        }
    }
}
int main() {
//  FR(); 
    re ll T=read();
    while(T--) {
        scanf("%s",ss+1);
        n=strlen(ss+1);
        m=read();init();
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        bool flag=0;
        for(re ll i=1;i<=m;i++) {
            flag^=1;
            memset(dp[flag],0,sizeof(dp[flag]));
            for(re ll j=0;j<(1<<n);j++) {
                for(re ll k=0;k<4;k++) {
                    dp[flag][nxt[j][k]]=(dp[flag][nxt[j][k]]+dp[flag^1][j])%mod;
                }
            }
        }
        memset(ans,0,sizeof(ans));
        for(re ll i=0;i<(1<<n);i++) {
            ans[__builtin_popcount(i)]=(ans[__builtin_popcount(i)]+dp[m&1][i])%mod;
        }
        for(re ll i=0;i<=n;i++) writeln(ans[i]);
    }
//  FC();
    return 0;
}
原文地址:https://www.cnblogs.com/20020723YJX/p/9381467.html