hdu3341 Lost‘s revenge(AC自动机上dp+hash优化状态数)

题意:

给定n个模式串,和一个匹配串,
现在你可以对匹配串重新排序,问最多能包含多少个模式串(可以重复累计)。
保证模式串和匹配串只出现ACGT四种字符。

数据范围:n<=50,每个模式串长度不超过10,匹配串长度不超过40.

解法:
统计出匹配串每种字符的数量
令d[i][a][c][g][t][x]表示前i步,acgt字符使用状态,当前位于节点x,的最大值
发下acgt的数量=i,因此步数的那一维可以去掉.

令d[a][c][g][t][x]为acgt使用状态,当前位于节点x,的最大值.
但是acgt的最大数量为40,40^4的空间显然不可能.

考虑到acgt的总和最大为40,有许多状态是不需要的,
如果每个字符10,那么每种字符当前使用数量的范围在[0,10],11,
总状态数为11^4<15000,所以将状态hash一下,
令id[i][j][k][l]为此时acgt数量分别为的i,j,k,l时的hash值,
那么只需要令d[i][j]表示acgt使用状态为i,当前位于节点j的最大值就行了.

然后在ac自动机上dp即可.
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
char s[maxm];
int id[45][45][45][45],idx;
int cnt[4];
int n;
void init_hash(){
    memset(id,0,sizeof id);
    idx=0;
    for(int i=0;i<=cnt[0];i++){
        for(int j=0;j<=cnt[1];j++){
            for(int x=0;x<=cnt[2];x++){
                for(int y=0;y<=cnt[3];y++){
                    id[i][j][x][y]=++idx;
                }
            }
        }
    }
}
int get(char c){
    if(c=='A')return 0;
    if(c=='C')return 1;
    if(c=='G')return 2;
    return 3;
}
struct AC{
    int a[maxm][4];
    int fail[maxm];
    int val[maxm];
    int tot=0;
    void init(){
        for(int i=0;i<=tot;i++){
            memset(a[i],0,sizeof a[i]);
            fail[i]=val[i]=0;
        }
        tot=0;
    }
    void add(char *s){
        int len=strlen(s);
        int node=0;
        for(int i=0;i<len;i++){
            int v=get(s[i]);
            if(!a[node][v])a[node][v]=++tot;
            node=a[node][v];
        }
        val[node]++;
    }
    void build(){
        queue<int>q;
        for(int i=0;i<4;i++){
            if(a[0][i]){
                q.push(a[0][i]);
                fail[a[0][i]]=0;
            }
        }
        while(!q.empty()){
            int x=q.front();q.pop();
            for(int i=0;i<4;i++){
                if(a[x][i]){
                    fail[a[x][i]]=a[fail[x]][i];
                    val[a[x][i]]+=val[a[fail[x]][i]];//因为可以重叠,所以要累加
                    q.push(a[x][i]);
                }else{
                    a[x][i]=a[fail[x]][i];
                }
            }
        }
    }
    //
    int d[maxm][15000+5];//d[i][j]表示当前在i节点,字符使用状态为j的最大值.
    int solve(){
        //init
        for(int i=0;i<=tot;i++){
            for(int j=1;j<=idx;j++){
                d[i][j]=-1;
            }
        }
        //dp
        d[0][1]=0;
        for(int i=0;i<=cnt[0];i++){
            for(int j=0;j<=cnt[1];j++){
                for(int k=0;k<=cnt[2];k++){
                    for(int l=0;l<=cnt[3];l++){
                        int now=id[i][j][k][l];
                        for(int x=0;x<=tot;x++){//枚举当前节点
                            if(d[x][now]==-1)continue;
                            if(i!=cnt[0]){//加一个A
                                int to=id[i+1][j][k][l];
                                int v=a[x][0];
                                d[v][to]=max(d[v][to],d[x][now]+val[v]);
                            }
                            if(j!=cnt[1]){//加一个C
                                int to=id[i][j+1][k][l];
                                int v=a[x][1];
                                d[v][to]=max(d[v][to],d[x][now]+val[v]);
                            }
                            if(k!=cnt[2]){//加一个G
                                int to=id[i][j][k+1][l];
                                int v=a[x][2];
                                d[v][to]=max(d[v][to],d[x][now]+val[v]);
                            }
                            if(l!=cnt[3]){//加一个T
                                int to=id[i][j][k][l+1];
                                int v=a[x][3];
                                d[v][to]=max(d[v][to],d[x][now]+val[v]);
                            }
                        }
                    }
                }
            }
        }
        //
        int ans=0;
        for(int i=0;i<=tot;i++){
            ans=max(ans,d[i][idx]);
        }
        return ans;
    }
}ac;
signed main(){
    int cas=1;
    while(scanf("%d",&n)!=EOF&&n){
        ac.init();
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            ac.add(s);
        }
        ac.build();
        //
        scanf("%s",s);
        int len=strlen(s);
        for(int i=0;i<4;i++)cnt[i]=0;
        for(int i=0;i<len;i++){
            if(s[i]=='A')s[i]=0;
            else if(s[i]=='C')s[i]=1;
            else if(s[i]=='G')s[i]=2;
            else if(s[i]=='T')s[i]=3;
            cnt[(int)s[i]]++;
        }
        init_hash();
        int ans=ac.solve();
        printf("Case %d: ",cas++);
        printf("%d\n",ans);
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值