HDU 3341 Lost's revenge(AC自动机+状态压缩DP)

40 篇文章 1 订阅
6 篇文章 0 订阅

Description
给出N个优良的基因段,每段长度小于等于10,只含有AGCT四种碱基。
现给一段基因片段S,|S|<=40。对其重排列后,最多能含有多少个优良基因,基因段可以有公共部分
Input
多组用例,每组用例第一行为一个整数N表示模式串个数,最后一行为文本串S,以N=0结束输入
Output
对于每组用例,输出对文本串重排列后最多能含有多少优良基因
Sample Input
3
AC
CG
GT
CGAT
1
AA
AAA
0
Sample Output
Case 1: 3
Case 2: 2
Solution
AC自动机,字符最长是40,只需要记录ACGT出现的次数。如果使用5维数组,显然超内存了。假设ACGT的总数分别为num[0],num[1],num[2],num[3]
那么对于ACGT的数量分别为a,b,c,d的状态可以记录为:a*(num[1]+1)(num[2]+1)(num[3]+1) + b*(num[2]+1)(num[3]+1)+ c(num[3]+1) +d,这样的状态最大就是11*11*11*11,复杂度也可以承受了。状态转移方程dp[next[i][k]][state+bit[k]]=max(dp[next[i][k]][state+bit[k]],dp[i][state]+end[next[i][k]])
Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 55
#define maxl 11
struct Trie
{
    int next[maxn*maxl][4],fail[maxn*maxl],end[maxn*maxl];
    int root,L;
    int inv(char c)//解码 
    {
        if(c=='A')return 0;
        if(c=='C')return 1;
        if(c=='G')return 2;
        if(c=='T')return 3;
    }
    int newnode()
    {
        for(int i=0;i<4;i++)
            next[L][i]=-1;
        end[L++]=0;
        return L-1;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char buf[])
    {
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++)
        {
            if(next[now][inv(buf[i])]==-1)
                next[now][inv(buf[i])]=newnode();
            now=next[now][inv(buf[i])];
        }
        end[now]++;
    }
    void build()
    {
        queue<int>Q;
        fail[root]=root;
        for(int i=0;i<4;i++)
            if(next[root][i]==-1)
                next[root][i]=root;
            else
            {
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        while(!Q.empty())
        {
            int now=Q.front();
            Q.pop();
            end[now]+=end[fail[now]];
            for(int i=0;i<4;i++)
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    int dp[maxn*maxl][11*11*11*11+11],bit[4],num[4];
    int solve(char buf[])
    {
        memset(num,0,sizeof(num));//初始化 
        int len=strlen(buf);
        for(int i=0;i<len;i++)//统计文本串中AGCT的个数 
            num[inv(buf[i])]++;
        bit[0]=(num[1]+1)*(num[2]+1)*(num[3]+1);
        bit[1]=(num[2]+1)*(num[3]+1);
        bit[2]=num[3]+1;
        bit[3]=1;
        memset(dp,-1,sizeof(dp));//初始化 
        dp[root][0]=0;
        for(int a=0;a<=num[0];a++)
            for(int b=0;b<=num[1];b++)
                for(int c=0;c<=num[2];c++)
                    for(int d=0;d<=num[3];d++)
                    {
                        int state=a*bit[0]+b*bit[1]+c*bit[2]+d*bit[3];//状态压缩 
                        for(int i=0;i<L;i++)//枚举所有节点 
                            if(dp[i][state]>=0)
                                for(int k=0;k<4;k++)
                                {
                                    if(k==0&&a==num[0]||k==1&&b==num[1]||k==2&&c==num[2]||k==3&&d==num[3])
                                        continue;
                                    dp[next[i][k]][state+bit[k]]=max(dp[next[i][k]][state+bit[k]],dp[i][state]+end[next[i][k]]); 
                                }
                    }
        int ans=0;
        int s=num[0]*bit[0]+num[1]*bit[1]+num[2]*bit[2]+num[3]*bit[3];
        for(int i=0;i<L;i++)//找到含优秀基因片段数最多的串 
            ans=max(ans,dp[i][s]);
        return ans;
    }
};
char buf[55];
Trie ac;
int main()
{
    int n;
    int res=1;
    while(scanf("%d",&n),n)
    {
        ac.init();//初始化 
        for(int i=0;i<n;i++)
        {
            scanf("%s",buf);
            ac.insert(buf);//插入模式串 
        }
        ac.build();//建树 
        scanf("%s",buf);
        printf("Case %d: %d\n",res++,ac.solve(buf));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值