hdu4057(ac自动机,状态压缩dp)

202 篇文章 0 订阅
154 篇文章 0 订阅

Rescue the Rabbit

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1790    Accepted Submission(s): 512


Problem Description
Dr. X is a biologist, who likes rabbits very much and can do everything for them. 2012 is coming, and Dr. X wants to take some rabbits to Noah's Ark, or there are no rabbits any more.

A rabbit's genes can be expressed as a string whose length is l (1 ≤ l ≤ 100) containing only 'A', 'G', 'T', 'C'. There is no doubt that Dr. X had a in-depth research on the rabbits' genes. He found that if a rabbit gene contained a particular gene segment, we could consider it as a good rabbit, or sometimes a bad rabbit. And we use a value W to measure this index.

We can make a example, if a rabbit has gene segment "ATG", its W would plus 4; and if has gene segment "TGC", its W plus -3. So if a rabbit's gene string is "ATGC", its W is 1 due to ATGC contains both "ATG"(+4) and "TGC"(-3). And if another rabbit's gene string is "ATGATG", its W is 4 due to one gene segment can be calculate only once.

Because there are enough rabbits on Earth before 2012, so we can assume we can get any genes with different structure. Now Dr. X want to find a rabbit whose gene has highest W value. There are so many different genes with length l, and Dr. X is not good at programming, can you help him to figure out the W value of the best rabbit.
 

Input
There are multiple test cases. For each case the first line is two integers n (1 ≤ n ≤ 10),l (1 ≤ l ≤ 100), indicating the number of the particular gene segment and the length of rabbits' genes.

The next n lines each line contains a string DNAi and an integer wi (|wi| ≤ 100), indicating this gene segment and the value it can contribute to a rabbit's W.
 

Output
For each test case, output an integer indicating the W value of the best rabbit. If we found this value is negative, you should output "No Rabbit after 2012!".
 

Sample Input
  
  
2 4 ATG 4 TGC -3 1 6 TGC 4 4 1 A -1 T -2 G -3 C -4
 

Sample Output
  
  
4 4 No Rabbit after 2012!
Hint
case 1:we can find a rabbit whose gene string is ATGG(4), or ATGA(4) etc. case 2:we can find a rabbit whose gene string is TGCTGC(4), or TGCCCC(4) etc. case 3:any gene string whose length is 1 has a negative W.

题意:给出一些模式串,每个串有一定的价值,现在要构造一个长度为M的串,问最大的价值为多少,每个模式串最多统计一次。

首先涉及到了字符串的匹配,要用到ac自动机,其次,求最大价值那么就要用状态压缩dp来解决了。网上的各种博客并没有一个把这一题讲解的很清晰的。。

首先来说ac自动机吧,建立ac自动机我用的二维数组模拟,用val[]数组来表示每一个子串的结束(可是存的数并不是1,2,3,4,5……这些,因为我们后边要用到状态压缩dp,那么这里我用2^i,也就是代码里的1<<i来表示,为何要这样表示?我们用位运算来模拟总共这n个子串如果存在就是1,不存在就是0,那么我们现在每个子串的结尾用2^i表示,我们想想,把2^i转化为2进制之后,是不是恰好都是只有1个1?这样来精确的表示存在第i个字串,这里跟后面的状态压缩dp有非常大的关联)

然后是建立自动机的过程,与正常的建立差不多,只不过也是val数组有变动,因为相当于在一个子串结束了恰好接上另一个子串的时候,难道val[]不变吗?如果不变那么我们建立ac自动机就没意义了。试想我们用val[ ch[p][i] ]  | =  val[ f [ ch [p][i] ] ];(f[]数组是失配指针数组),为何要用或?因为一个子串的尾节点的val值一定是1到1<<n之间的化为二进制只有一位为1的某数,现在它连接了另一个单词,那么在二进制里面又加上了另一个单词,不过val里边却只有一个1,这样我们用或的位运算来恰好接上另一个1来表示两个子串的相连。

然后是dp部分,我们用dp[i][j][k]表示状态,就是要构造长度为i的到了ac自动机里第j个节点的对应状态为k的(当然k一定要用二进制的眼光来看,0表示某个子串不存在,1表示存在)状态是否成立,如果成立,那么让他等于1,否则继续。

if(dp[(i+1)&1][j][hh])  dp[i&1][ch[j][k]][hh|val[ch[j][k]]]=1;

这是dp部分的精华,因为上一个状态成立的之后,代表下一个状态能推出来的状态也必然成立,推出来的val[ch[j][k]]就是我介绍为什么要按上面的方式存储val的原因,它记录了某一个节点包含多少个子串的全部信息(当然是转化为二进制之后,为1代表有,0代表没有咯)

当然,由于空间不能开爆内存,所以采取滚动数组的方式,将dp[i][j][k]的i只开了2,,相当于模拟,同时大幅减小了内存。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
typedef long long ll;
const int CH =4,NODE = 1005;
int m,n;
int idx(char x)
{
    if(x=='A')return 0;
    else if(x=='C')return 1;
    else if(x=='G')return 2;
    return 3;
}
int ch[NODE][CH],f[NODE],val[NODE],sz,v[NODE];

int node()
{
    memset(ch[sz],0,sizeof(ch[sz]));
    val[sz]=0;
    return sz++;
}
void init()
{
    sz=0;
    node();
    memset(f,0,sizeof(f));
}
void ins(char *s,int i)
{
    int u=0;
    for(; *s; s++)
    {
        int c=idx(*s);
        if(!ch[u][c])
            ch[u][c]=node();
        u=ch[u][c];
    }
    val[u]=1<<i;
}

int queu[NODE];
void getfail()
{
    int l=0,r=0;
    queu[r++]=0;
    while(l<r)
    {
        int p=queu[l++];
        for(int i=0; i<4; i++)
        {
            if(ch[p][i]==0)
                ch[p][i]=ch[f[p]][i];
            else
            {
                int q=ch[p][i];
                if(p)
                    f[q]=ch[f[p]][i];
                val[q]|=val[f[q]];
                queu[r++]=q;
            }
        }
    }
}
bool dp[2][1005][1<<10];

int get(int x)
{
    int ans=0;
    for(int i=0; i<(1<<n); i++)
        if(x&(1<<i))//这里的位运算也非常巧妙
            ans+=v[i];
    return ans;
}

void solve()
{
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    for(int i=1; i<=m; i++)
    {
        memset(dp[i&1],0,sizeof(dp[i&1]));
        for(int j=0; j<=sz; j++)
        {
            for(int k=0; k<4; k++)
            {
                for(int hh=0; hh<(1<<n); hh++)
                    if(dp[(i+1)&1][j][hh])
                        dp[i&1][ch[j][k]][hh|val[ch[j][k]]]=1;
            }
        }
    }
    int ans=-999999999;
    for(int i=0; i<=sz; i++)
        for(int j=0; j<(1<<n); j++)
            if(dp[m&1][i][j])
                ans=max(ans,get(j));
    if(ans<0)
        printf("No Rabbit after 2012!\n");
    else printf("%d\n",ans);

}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0; i<n; i++)
        {
            char ss[105];
            scanf("%s%d",ss,&v[i]);
            ins(ss,i);
        }
        getfail();
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值