@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]\)
我們通過列舉 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。