题解:P7152 [USACO20DEC] Bovine Genetics G

· · 题解

洛谷P7152 [USACO20DEC] Bovine Genetics G

首先可以转换一下题意。

我们对于一个串,需要将这个串反向划分成几段,这样比较直观的求出原来的串。

就像样例 GATAGTT -> GA|TAG|T|T 就是一种反向划分的例子。

我们看得出来划分受到几个约束的限制:

  1. 块内不能出现连续的字符,否则原串一定会从这个地方断开。

  2. 相邻块间,左块的左端点与右块相等。

我们由上面两种限制设计连续段 DP 状态。

考虑 dp_{i,j,b,c} 表示当前处理到了第 i 位,当前这一段的开头是 j,结尾是 b,上一段的开头是 c

转移有两种,一种是新开一段,一种是在后面加。

dp_{i,s_i,b,c} \leftarrow dp_{i-1,s_{i-1},b,c}\ (s_{i-1} \ne s_i) \\ dp_{i,s_i,s_i,b} \leftarrow dp_{i-1,s_{i-1},b,c}\ (s_{i-1} = c)

后面括号里的是限制条件,分别对应以上提到的两条。

\Large \mathscr{Code}
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100, MOD = 1e9 + 7;
int n, m, dp[N][4][4][4], ans;
string s, ori = "AGCT";
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> s;
    int now = 1;
    for (int j = 0; j < 4; j++) {
        if (s[0] != '?' && s[0] != ori[j]) continue;
        for (int i = 0; i < 4; i++) {
            dp[0][j][j][i] = 1;
        }
    }
    for (int i = 1; i < s.size(); i++) {
        for (int j = 0; j < 4; j++) {
            if (s[i] != '?' && s[i] != ori[j]) continue; // 如果有 '?' 的话直接看成四个都转移
            for (int b = 0; b < 4; b++) {
                for (int c = 0; c < 4; c++) {
                    for (int k = 0; k < 4; k++) {
                        if (k != j) dp[i][j][b][c] = (dp[i][j][b][c] + dp[i - 1][k][b][c]) % MOD;
                        if (k == c) dp[i][j][j][b] = (dp[i][j][j][b] + dp[i - 1][k][b][c]) % MOD;
                    }
                }
            }
        }
    }
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            ans = (ans + dp[s.size() - 1][j][i][j]) % MOD;
        }
    }
    cout << ans;
    return 0;
}