Submit: 397 Solved: 206
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
GTC
10
Sample Output
22783
528340
497452
HINT
Source
首先想一下LCS的转移方程
$$lcs[i][j]=max \begin{cases} lcs[i-1][j-1]+1 & \text{if t[i]=s[j]} \\ lcs[i-1][j] \\ lcs[i][j-1] \end{cases}$$
这样的话,当$i$确定是,$lcs[i][j]$和$lcs[i][j-1]$最多相差$1$
且题目中说$|S|<= 15$,因此我们考虑把差分后的lcs数组状压起来
那么如何统计答案呢?
设$f[i][sta]$表示在第$i$个位置,此时lcs的状态为$sta$的方案数,
然后我们枚举一下这个位置选ACGT中的哪个
设$trans[sta'][A/C/G/T]$为在$sta$状态表示的lcs后加了ACGT中的一个后的状态,这个很显然可以预处理得到
那么转移方程为
$$f[i][ trans[sta][k] ] += f[i - 1][sta] $$
$$f[0][0] = 1$$
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1001, mod = 1e9 + 7; char S[16], SS[] = {"ACGT"}; int a[16], f[MAXN][(1 << 15) + 2], trans[(1 << 15) + 2][5], N, Len, limit, ans[111]; int tmp[2][16]; int solve(int sta, int ch) { int ret = 0; memset(tmp, 0, sizeof(tmp)); for(int i = 0; i < N; i++) tmp[0][i + 1] = tmp[0][i] + ((sta >> i) & 1 ); for(int i = 1; i <= N; i++) { int mx = 0; if(a[i] == ch) mx = tmp[0][i - 1] + 1; mx = max( max(mx, tmp[0][i]), tmp[1][i-1]); tmp[1][i] = mx; } for(int i = 0; i < N; i++) ret += (1 << i) * (tmp[1][i + 1] - tmp[1][i]); return ret; } int main() { #ifdef WIN32 freopen("a.in", "r", stdin); #endif int QWQ;scanf("%d", &QWQ); while(QWQ--) { memset(f, 0, sizeof(f));memset(ans, 0, sizeof(ans)); scanf("%s", S + 1); N = strlen(S + 1); limit = (1 << N) - 1; for(int i = 1; i <= N; i++) for(int j = 0; j < 4; j++) if(S[i] == SS[j]){a[i] = j + 1;break;} scanf("%d", &Len); f[0][0] = 1; for(int sta = 0; sta <= limit; sta++) for(int j = 1; j <= 4; j++) trans[sta][j] = solve(sta, j); for(int i = 1; i <= Len; i++) for(int sta = 0; sta <= limit; sta++) for(int k = 1; k <= 4; k++) f[i][ trans[sta][k] ] = (f[i][ trans[sta][k] ] + f[i - 1][sta]) % mod; for(int sta = 0; sta <= limit; sta++) ans[__builtin_popcount(sta)] = (ans[__builtin_popcount(sta)] + f[Len][sta]) % mod; //这个函数是算出sta中1的个数 for(int i = 0; i <= N; i++) printf("%d\n", ans[i] % mod); } return 0; }