bzoj 3864: Hero meet devil [dp套dp]

3864: Hero meet devil

题意:

给你一个只由AGCT组成的字符串S (|S| ≤ 15),对于每个0 ≤ .. ≤ |S|,问
有多少个只由AGCT组成的长度为m(1 ≤ m ≤ 1000)的字符串T,使得\(LCS(T,S) = i\)


dp套dp!

通过一个外层的dp来计算使得另一个dp方程(子dp)最终结果为特定值输入数

一位一位确定子dp的输入,记录子dp的状态值

子dp:

\(d(i,j)\)表示\(LCS(T[1,i],S[1,j])\),对第二维差分,\(j\)\(j-1\)只可能相差0/1,可以状压

外层dp:

\(f(i,s)\)表示确定到T的第i位,子dp状态值为s的方案数

预处理子dp每种状态值加一个字符后的转移,然后进行外层dp即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1005, mo = 1e9+7;
inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, m; char str[20], c[10] = "ACGT";
int trans[1<<15][4], one[1<<15], f[2][1<<15];
void init() {
    static int d[20], g[20];
    for(int s=0; s < 1<<n; s++) {
        if(s) one[s] = one[s ^ (s&-s)] + 1;
        for(int j=0; j<n; j++) d[j+1] = d[j] + bool(s & (1<<j));
        for(int k=0; k<4; k++) {
            for(int j=1; j<=n; j++) {
                g[j] = max(g[j-1], d[j]);
                if(c[k] == str[j]) g[j] = max(g[j], d[j-1]+1);
            }
            trans[s][k] = 0;
            for(int j=0; j<n; j++) if(g[j+1] - g[j]) trans[s][k] |= 1<<j; 
        }
    }
}
int ans[N];
int main() {
    freopen("in", "r", stdin);
    int T = read();
    while(T--) {
        scanf("%s", str+1); m = read(); n = strlen(str+1);
        init();
        memset(ans, 0, sizeof(ans));
        memset(f, 0, sizeof(f));
        f[0][0] = 1; int p = 0;
        for(int i=1; i<=m; i++, p ^= 1) {
            memset(f[p^1], 0, sizeof(f[p^1]));
            for(int s=0; s < 1<<n; s++)
                for(int k=0; k<4; k++) (f[p^1][trans[s][k]] += f[p][s]) %= mo;
        }
        for(int s=0; s < 1<<n; s++) (ans[one[s]] += f[p][s]) %= mo;
        for(int i=0; i<=n; i++) printf("%d\n", ans[i]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值