1. 程式人生 > >@BZOJ - [email protected] Hero meet devil

@BZOJ - [email protected] Hero meet devil

目錄


@description - [email protected]

給定一個基因串 S(僅由 A,G,C,T 組成的串)。給出另一個基因串 T 的長度 m。
對於每一個 0 <= i <= |S|,求出所有 4^m 種可能的基因串 T 有多少滿足 LCS(S, T) = i。
LCS:最長公共子序列。

input


多組資料。第一行輸入資料組數 T,T <= 5。
接下來每組資料,第一行一個基因串 S,|S| <= 15。第二行一個整數 m, m <= 1000。

output
對於每組資料,每一行輸出當 i = 0, 1,...,|S| 相應的答案,膜 10^9 + 7。

sample input
1
GTC
10
sample output
1
22783
528340
497452

@[email protected]

好像學名叫作 dp 套 dp。

Candy? dalao 給出了這種型別的題目的一般化描述:

通過一個外層的 dp 來計算使得另一個 dp 方程 (子dp) 最終結果為特定值的輸入數。

甚至還給出這種這種問題的一般化解法:

一位一位確定子 dp 的輸入,記錄子 dp 的狀態值。

我們考慮求兩個串 S,T 正常 LCS 的求解過程:
\[dp(i,j)=\begin{cases} 0&i==0或j==0\\ \max\{dp(i, j-1), dp(i-1, j)\}&S[i]\not =T[j]\\ dp(i-1, j-1) + 1 & S[i] = T[j] \end{cases}\]

S 串是已知的。因此我們大致的思路是,從前往後一位一位地確定 T 串的值,同時維護 dp 陣列不同取值對應的方案數。

聽起來好像很抽象。
\(f[s][j]\)

表示 \(\{dp[0][j], dp[1][j], \dots , dp[n][j]\}\) 的狀態為 \(s\) 的方案數。
我們通過列舉 T 串第 j 位的字元,算出 \(\{dp[0][j+1], dp[1][j+1], \dots , dp[n][j+1]\}\) 對應的狀態 \(s'\),再由 \(f[s][j]\) 轉移到 \(f[s'][j+1]\)

聽起來好像還是很抽象。不管了我們看一個更重要的問題。
\(dp[i][j]\) 的值可能很波動,怎麼把這個狀態存下來呢?
這個時候就涉及到 \(LCS\) 這玩意兒本身的性質:
\[dp[i][j] = dp[i-1][j] 或 dp[i-1][j]+1\]
於是我們把 dp 陣列差分一下,得到的差分陣列就是 01 陣列了,就可以正常狀壓了。
怎麼說明上面那個性質呢?可以從 \(LCS\) 的定義感性認知一下。

如果真的暴力做的話,時間複雜度是 \(O(T*2^{|S|}*|S|*m*4)\),是肯定 TLE 的。
怎麼辦呢?我們轉移 \(f\) 的時候,先把所有的情況 \(O(4*|S|*2^{|S|})\) 預處理了,轉移的時候複雜度就是 \(O(2^{|S|}*m*4)\) 的啦。

@accepted [email protected]

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXS = 15;
const int MAXM = 1000;
const int MOD = int(1E9) + 7;
const char DNA[] = {'A', 'C', 'G', 'T'};
int bits[1<<MAXS], trans[1<<MAXS][4];
//原來 trans 是 transfer 的簡寫,而不是 translate 啊qwq
//我英語真的很差 qwq 
char S[MAXS + 5]; int m;
int f[2][1<<MAXS], ans[MAXS + 5];
int a[MAXS + 5], b[MAXS + 5];
void solve() {
    scanf("%s%d", S, &m);
    int len = strlen(S), t = (1<<len);
    for(int s=0;s<t;s++) { 
        f[0][s] = 0;
        for(int k=0;k<4;k++) {
            for(int l=0;l<len;l++)
                a[l+1] = a[l] + ((s & (1<<l)) ? 1 : 0); 
            for(int l=0;l<len;l++)
                b[l+1] = (DNA[k] == S[l]) ? a[l] + 1 : max(a[l+1], b[l]);
            int s0 = 0;
            for(int l=len;l>=1;l--)
                s0 = (s0 << 1) | (b[l] - b[l-1]);
            trans[s][k] = s0;
        }
    } 
    f[0][0] = 1;
    for(int i=1;i<=m;i++) {
        for(int s=0;s<t;s++)
            f[i&1][s] = 0;
        for(int s=0;s<t;s++)
            for(int k=0;k<4;k++)
                f[i&1][trans[s][k]] = (f[i&1][trans[s][k]] + f[i&1^1][s])%MOD;
    }
    for(int i=0;i<=len;i++)
        ans[i] = 0;
    for(int s=0;s<t;s++)
        ans[bits[s]] = (ans[bits[s]] + f[m&1][s])%MOD;
    for(int i=0;i<=len;i++)
        printf("%d\n", ans[i]);
}
void init() {
    for(int i=1;i<=(1<<MAXS);i++)
        bits[i] = bits[i>>1] + (i&1);
}
int main() {
    init();
    int T; scanf("%d", &T);
    for(int i=1;i<=T;i++)
        solve();
}

@[email protected]

該死的運算子優先順序,C++ 可真是神奇 QAQ。
yhn 學長好像不是用 dp 套 dp 的思想來想的,不過也 AC 了這道題?
不管了不管了 qwq。