HDU 4899 Hero meet devil(状压DP)(2014 Multi-University Training Contest 4)

Problem Description
There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and can’t refuse any request from the devil. Also, this devil is looking like a very cute Loli.

After the ring has been destroyed, the devil doesn't feel angry, and she is attracted by z*p's wisdom and handsomeness. So she wants to find z*p out.

But what she only knows is one part of z*p's DNA sequence S leaving on the broken ring.

Let us denote one man's DNA sequence as a string consist of letters from ACGT. The similarity of two string S and T is the maximum common subsequence of them, denote by LCS(S,T).

After some days, the devil finds that. The kingdom's people's DNA sequence is pairwise different, and each is of length m. And there are 4^m people in the kingdom.

Then the devil wants to know, for each 0 <= i <= |S|, how many people in this kingdom having DNA sequence T such that LCS(S,T) = i.

You only to tell her the result modulo 10^9+7.
 
Input
The first line contains an integer T, denoting the number of the test cases.
For each test case, the first line contains a string S. the second line contains an integer m.

T<=5
|S|<=15. m<= 1000.
 
Output
For each case, output the results for i=0,1,...,|S|, each on a single line.
 
题目大意:字符集中只有“ATCG”四种字符,现在给你一个字符串S。问长度为m的所有字符串T中,最长公共子序列lcs(S, T) = i,其中 i 取遍0~|S|。|S|为S的长度。
思路:令state = lcs(S, T),其中state为lcs在S中的位置的信息再状态压缩后的值。总之这是一个状态压缩DP。
如果用dp[i][state]来表示lcs(S, T[1..i])的T[1..i]的数量,那么计算出来的结果会有重复导致无法得出正确的结果。
定义newlcs(S, T) = lcs(S, T)的最小的state。
举个栗子:S = AATTCCGG
那么对于T = ATCG
newlcs(S, T) = pos[0, 2, 4, 6],有且只有这一个解(从0开始计数)
这样,用dp[i][state]来表示newlcs(S, T[1..i])的T[1..i]的数量。那么对于每个T[1..i],有且只有一个dp[i][state]和它对应,其中state = newlca(S, T[1..i])。
那么就可以花O(2^n*n*4)的时间来预处理出每个state加上一个字符得到的新state。
再花O(m*2^n*4)的时间用滚动数组做状态压缩DP。
再统计一下答案,解决。
PS:代码里面__builtin_popcount()是G++内置函数,用于求一个数的二进制中1的个数。
 
代码(1281MS):
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int MAXN = 20;
 8 const int MOD = 1e9 + 7;
 9 
10 char dic[] = "ACTG";
11 int add[1 << 15][4];
12 int dp[2][1 << 15];
13 int pre[MAXN], lcs[MAXN], ans[MAXN];
14 char s[MAXN];
15 int T, n, m;
16 
17 inline void update_add(int &a, int b) {
18     a += b;
19     if(a >= MOD) a -= MOD;
20 }
21 
22 void init() {
23     for(int state = 0; state < (1 << n); ++state) {
24         pre[0] = 0;
25         for(int i = 1; i <= n; ++i) pre[i] = pre[i - 1] + ((state >> (i - 1)) & 1);
26         for(int k = 0; k < 4; ++k) {
27             for(int i = 1; i <= n; ++i) {
28                 if(s[i] == dic[k]) lcs[i] = pre[i - 1] + 1;
29                 else lcs[i] = max(lcs[i - 1], pre[i]);
30             }
31             int &t = add[state][k] = 0;
32             for(int i = 1; i <= n; ++i)
33                 t |= ((lcs[i] != lcs[i - 1]) << (i - 1));
34         }
35     }
36 }
37 
38 void solve() {
39     int *now = dp[0], *next = dp[1];
40     memset(next, 0, (1 << n) * sizeof(int));
41     next[0] = 1;
42     for(int _ = 0; _ < m; ++_) {
43         swap(now, next);
44         memset(next, 0, (1 << n) * sizeof(int));
45         for(int state = 0; state < (1 << n); ++state) if(now[state])
46             for(int k = 0; k < 4; ++k)
47                 update_add(next[add[state][k]], now[state]);
48     }
49     memset(ans, 0, sizeof(ans));
50     for(int state = 0; state < (1 << n); ++state) {
51         update_add(ans[__builtin_popcount(state)], next[state]);
52     }
53     for(int i = 0; i <= n; ++i)
54         printf("%d\n", ans[i]);
55 }
56 
57 int main() {
58     scanf("%d", &T);
59     while(T--) {
60         scanf("%s%d", s + 1, &m);
61         n = strlen(s + 1);
62         init();
63         solve();
64     }
65 }
View Code

 

转载于:https://www.cnblogs.com/oyking/p/3888867.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值