hdu 3341 AC自动机+DP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yqdjl6/article/details/83117541

这道题就是给你N个单词,一个字符串,问你将这个字符串重排,能组成包含单词次数最多的字符串有多长。这道题的DP方程有点意思,因为只有四个字符ACGT,字符串的长度不超过40,所以我们可以dp[i][a][b[][c][d],表示匹配了i个字符,用了分别用了a,b,c,d个ACGT,能包含的最多的单词。其中但dp[500][40][40][40][40]的内存,但我们可以转化一下,转化成在某种进制下的计数,我们首先计算每个字符在字符串中出现的次数,每增加一个A,我们就增加(num[1]+1)(num[2]+1)(num[3]+1),每增加一个T,我们就增加(num[2]+1)*(num[3]+1),每增加一个C,我们就增加(num[3]+1),每增加一个G,我们就增加1,和状态压缩的意思差不多,DP方程列出来就好说了

#include<bits/stdc++.h>
using namespace std;
using LL = int64_t;
const int maxnode=5005;
const int sigma_size=4;
char s[maxnode];

struct Node {
    int son[sigma_size];
    int val,fail;
}ch[maxnode];
int dp[505][15000];
struct AC {
    int sz=1;
    queue<int>Q;
    void init(int x) {ch[x].fail=ch[x].val=0;memset(ch[x].son,0,sizeof(ch[x].son));}
    int idx(char c) {
        if(c=='A') return 0;
        if(c=='T') return 1;
        if(c=='C') return 2;
        if(c=='G') return 3;
    }

    void insert(char s[],int v) {
        int u=0,n=strlen(s);
        for(int i=0;i<n;i++) {
            int c=idx(s[i]);
            if(!ch[u].son[c]) {
                init(sz);
                ch[u].son[c]=sz++;
            }
            u=ch[u].son[c];
        }
        ch[u].val++;
    }

    void build() {
        for(int i=0;i<sigma_size;i++) if(ch[0].son[i]) Q.push(ch[0].son[i]);
        while(!Q.empty()) {
            int now=Q.front();Q.pop();
            int fail=ch[now].fail;
            ch[now].val+=ch[fail].val;
            for(int i=0;i<sigma_size;i++) {
                int nxt=ch[now].son[i];
                if(nxt) {
                    ch[nxt].fail=ch[fail].son[i];
                    Q.push(nxt);
                }
                else ch[now].son[i]=ch[fail].son[i];
                //ch[ch[now].son[i]].val=ch[ch[ch[now].fail].son[i]].val;
            }
        }
    }

    int solve(char s[]) {
        int num[5]={0},cnt[5],len=strlen(s);
        for(int i=0;i<len;i++) num[idx(s[i])]++;
        cnt[0]=(num[1]+1)*(num[2]+1)*(num[3]+1);
        cnt[1]=(num[2]+1)*(num[3]+1);
        cnt[2]=(num[3]+1);
        cnt[3]=1;
        memset(dp,-1,sizeof(dp));
        dp[0][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 now=a*cnt[0]+b*cnt[1]+c*cnt[2]+d*cnt[3];
                        for(int i=0;i<sz;i++) {
                            if(dp[i][now]>=0) {
                                for(int k=0;k<4;k++) {
                                    if(k==0&&a==num[0]) continue;
                                    if(k==1&&b==num[1]) continue;
                                    if(k==2&&c==num[2]) continue;
                                    if(k==3&&d==num[3]) continue;
                                    dp[ch[i].son[k]][now+cnt[k]]=max(dp[ch[i].son[k]][now+cnt[k]],dp[i][now]+ch[ch[i].son[k]].val);
                                }
                            }
                        }
                    }
                }
            }
        }
        int ans=0,now=0;
        for(int i=0;i<4;i++) now=now+num[i]*cnt[i];
        for(int i=0;i<sz;i++) ans=max(ans,dp[i][now]);
        return ans;
    }

};

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int n,kase=1;
    while(cin>>n&&n) {
        AC ans;ans.init(0);
        for(int i=1;i<=n;i++) {
            cin>>s;
            ans.insert(s,1);
        }
        ans.build();
        cin>>s;
        cout<<"Case "<<kase++<<": "<<ans.solve(s)<<"\n";
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yqdjl6/article/details/83117541