【训练题37:动态规划】Bovine Genetics G | 洛谷 P7152 | USACO20DEC

B o v i n e   G e n e t i c s   G \color{green}Bovine\ Genetics\ G Bovine Genetics G

补充

  • 一道比较中高级的 d p dp dp 题,蒟蒻选手准备补一下该题题解
  • 【链接】Bovine Genetics G

正题

  • 【题意】
    有一个长度为 N N N 的基因段,即每一个位置的元素为 A , G , C , T A,G,C,T A,G,C,T 中的一个。
    首先,我们把每个相邻字母相同的位置进行分割,然后分割的每一段都翻转一下,再拼起来。
    A G G A T T T ↓ A G ∣ G A T ∣ T ∣ T ↓ G A ∣ T A G ∣ T ∣ T ↓ G A T A G T T \begin{matrix} AGGATTT\\ \downarrow\\ AG|GAT|T|T\\ \downarrow\\ GA|TAG|T|T\\ \downarrow\\ GATAGTT \end{matrix} AGGATTTAGGATTTGATAGTTGATAGTT
    那么,原串 S = A G G A T T T S=AGGATTT S=AGGATTT 就变成了果串 T = G A T A G T T T=GATAGTT T=GATAGTT
    给定一个损坏的果串 T T T,某些位置为 ? ? ? 表示为 A , G , C , T A,G,C,T A,G,C,T 中的一个。
    问你,该果串合法的原串 的数量为多少?取模 1 e 9 + 7 1e9+7 1e9+7
  • 【范围】
    1 ≤ N ≤ 1 0 5 1\le N\le 10^5 1N105

思路

  • 考虑一个原串到果串的操作:
    A . . . G ∣ G . . . C ∣ C . . . G ∣ G . . . D ∣ D ↓ G . . . A ∣ C . . . G ∣ G . . . C ∣ D . . . G ∣ D \begin{matrix} A...G|G...C|C...G|G...D|D\\ \downarrow\\ G...A|C...G|G...C|D...G|D \end{matrix} A...GG...CC...GG...DDG...AC...GG...CD...GD
    结论一:原串按照分割原则之后形成的小段 ( A . . . G ) ( G . . . C ) ( C . . . G ) ( D . . . ) (A...G)(G...C)(C...G)(D...) (A...G)(G...C)(C...G)(D...)
    那么,每一个小段的相邻位置一定是不同的。否则,该段还能分成更细的小段。
    .
    问方案数,大概率就是一个类似计数的 d p dp dp ,考虑怎么去设 d p dp dp 的状态。
    考虑到,我们必须进行一段一段地分析
    首先我们假设设 d p [ i ] [ x ] [ y ] dp[i][x][y] dp[i][x][y] 表示目前枚举到第 i i i 个位置,最后一个小段的头和尾分别为字母 x x x 和字母 y y y
    那么新进来一个末尾字母 t t t ,该字母应该和最后一段合并,还是新开一个小段? 这个时候貌似状态不够了。我们怎么新加状态呢?
    .
    结论二:对于果串,目前的两个小段的首尾字母为 ( Z . . . ) ( X . . . Y ) (Z...)(X...Y) (Z...)(X...Y),该两段不一定合法
    新加一个字母 t t t,变成了 ( Z . . . ) ( X . . Y t ) (Z...)(X..Yt) (Z...)(X..Yt)
    这两个小段能合并的情况就是 Z = t Z=t Z=t,于是变成了 ( t . . . ) ( X . . . t ) (t...)(X...t) (t...)(X...t),该两段合法。
    .
    那么问题来了:为什么我们不记录果串为 ( Z . . . K ) ( X . . . Y ) (Z...K)(X...Y) (Z...K)(X...Y) 呢?因为如果前面还存在一些合法的段的话,那么果串的样子就是 ( K . . . ? ) ( Z . . . K ) ( X . . . Y ) (K...?)(Z...K)(X...Y) (K...?)(Z...K)(X...Y),也就是说这个 K K K 是不需要考虑的, K K K 要求取遍所有的答案。
    .
    所以我们记录 d p [ i ] [ x ] [ y ] [ z ] dp[i][x][y][z] dp[i][x][y][z] 表示目前位置为 i i i,最后两段的样子为: ( Z . . . ) ( X . . . Y ) (Z...)(X...Y) (Z...)(X...Y),此时的方案数。
    【考虑转移】
    (合并):如果新进来一个字母 t t t ,如果 Z = Y Z=Y Z=Y 已经成立了,那么 t t t 可以自成一段,变成 ( X . . . ) ( Z ) (X...)(Z) (X...)(Z)
    (不合并):如果 t ≠ Y t\ne Y t=Y,那么根据结论一,我们可以把这个字母加到这个段里去,变成 ( Z . . . ) ( X . . . t ) (Z...)(X...t) (Z...)(X...t)
    【考虑答案】
    合法答案,就是到末尾位置,最后两段为 ( X . . . ) ( ? . . . X ) (X...)(?...X) (X...)(?...X)
    .
    还有一个问题,我们以上都没有考虑问号字母。问号字母咋处理呢?我们把问号字母当做四种不同的字母,分别去转移即可。

代码

  • 时间复杂度: O ( 4 4 × n ) O(4^4\times n) O(44×n)
char aa[MAX];
int dp[MAX][5][5][5];
map<char,int>Z;
int main(){
    scanf("%s",aa+1);
    int n = strlen(aa+1);
    Z['A'] = 0;
    Z['G'] = 1;
    Z['C'] = 2;
    Z['T'] = 3;
    Z['?'] = 4;
    for(int x = 0;x < 4;++x)for(int y = 0;y < 4;++y){		/// 第一位稍微搞搞
        if(Z[aa[1]] == 4 || Z[aa[1]] == x)dp[1][x][x][y] = 1;
    }
    for(int i = 2;i <= n;++i)for(int x = 0;x < 4;++x)for(int y = 0;y < 4;++y)for(int z = 0;z < 4;++z){
        if(Z[aa[i]] == 4){
            for(int t = 0;t < 4;++t){	/// 四种字母分别去跑
                if(y == z)dp[i][t][t][x] = ((ll)dp[i][t][t][x] + dp[i-1][x][y][z]) % MOD;
                if(t != y)dp[i][x][t][z] = ((ll)dp[i][x][t][z] + dp[i-1][x][y][z]) % MOD;
            }   
        }else{
            int t = Z[aa[i]];			/// 跑一种字母即可
            if(y == z)dp[i][t][t][x] = ((ll)dp[i][t][t][x] + dp[i-1][x][y][z]) % MOD;
            if(t != y)dp[i][x][t][z] = ((ll)dp[i][x][t][z] + dp[i-1][x][y][z]) % MOD;
        }
    }
    ll res = 0;
    for(int x = 0;x < 4;++x)for(int y = 0;y < 4;++y)res = (res + (ll)dp[n][x][y][y]) % MOD;
    printf("%lld",res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值