目录
@description - translation@
给定一个基因串 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
@solution@
好像学名叫作 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}\]
![](/qrcode.jpg)
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 code@
#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();
}
@details@
该死的运算符优先级,C++ 可真是神奇 QAQ。
yhn 学长好像不是用 dp 套 dp 的思想来想的,不过也 AC 了这道题?
不管了不管了 qwq。