• HDU 3341 Lost's revenge ( Trie图 && 状压DP && 数量限制类型 )


    题意 : 给出 n 个模式串,最后给出一个主串,问你主串打乱重组的情况下,最多能够包含多少个模式串。

    分析 : 如果你做过类似 Trie图 || AC自动机 + DP 类似的题目的话,那么这道题相对之前的对于主串的“构造”过程加上了一个限制,那就是字符的元素的有限制的,那么DP的状态就不能用长度来表示状态( 类比 POJ 2778 ),所以写出了一个错误的根据长度DP的代码

    for(int i=0; i<len; i++){
        for(int j=0; j<ac.Size; j++){
            if(dp[i][j] >= 0){
                for(int k=0; k<4; k++){
                    if(Lter[i][j][k] > 0){///表示 i、j 状态下,0123代表的“ATGC”字符数还剩多少,但是这是错的,因为在长度为 i 停留在当前节点
                                          ///j 的字符串可能有多种,而这些字符串所拥有的剩余字符数是不一样的,不能单纯只用Lter[i][j][k]表示
                                          ///实际上这只是我自己的理解,我没有打表跟踪过错误数据,你可以自己想想为什么这样子不行……
                        int newi = i + 1;
                        int newj = ac.Node[j].Next[k];
                        dp[newi][newj] = max(dp[newi][newj], dp[i][j] + ac.Node[newj].cnt);
                        for(int l=0; l<4; l++)
                            Lter[newi][newj][l] = Lter[i][j][l];
                        Lter[newi][newj][k]--;
                    }
                }
            }
        }
    }
    View Code

    那要如何定义DP状态呢?一般来说对于这样的数量和所在节点状态是关键点,所以我们可以DP[A][T][G][C][Node]前四维表示ATGC数量,最后一维表示当前状态停留在节点 Node ,但是这样子空间会爆炸,这时候就需要压缩一下状态,考虑压缩前四维,网上有很多利用进制的压缩,弱智的我有点不理解,所以还是用了普通的Hash,即利用一个四维数组 Hash[11][11][11][11] ( 每一个字母最多就是 10 个,所以这样开数组 ) ,然后只需要统计主串各个种类字符的数量,就能打出一个 Hash 表,将原本五维DP压成二维DP,DP[i][j] 表示各个字符数量状态为 i 且停留在 j 节点的最多包含模式串个数,则状态转移方程为 ( 一个节点状态能转到"ATGC"四个状态,那么以转到字符 ' A ' 为例 )

    DP[ Hash[A+1][G][T][C] ][j] = max( DP[ Hash[A+1][G][T][C] ][j], DP[i][j] + Trie[j]['A'].cnt )

    DP初始状态为 DP[0][0] = 0、DP[0~最大的Hash值数量][0~Trie图上的节点个数] = -1

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int Letter  = 26;
    const int Max_Tot = 50 * 10 + 5;
    int mp[128];
    int Hash[41][41][41][41];
    
    struct Aho{
        struct StateTable{
            int Next[Letter];
            int fail, cnt;
        }Node[Max_Tot];
        int Size;
        queue<int> que;
    
        inline void init(){
            while(!que.empty()) que.pop();
            memset(Node[0].Next, 0, sizeof(Node[0].Next));
            Node[0].fail = Node[0].cnt = 0;
            Size = 1;
        }
    
        inline void insert(char *s){
            int now = 0;
            for(int i=0; s[i]; i++){
                int idx = mp[s[i]];
                if(!Node[now].Next[idx]){
                    memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                    Node[Size].fail = Node[Size].cnt = 0;
                    Node[now].Next[idx] = Size++;
                }
                now = Node[now].Next[idx];
            }
            Node[now].cnt++;
        }
    
        inline void BuildFail(){
            Node[0].fail = 0;
            for(int i=0; i<Letter; i++){
                if(Node[0].Next[i]){
                    Node[Node[0].Next[i]].fail = 0;
                    que.push(Node[0].Next[i]);
                }else Node[0].Next[i] = 0;///必定指向根节点
            }
            while(!que.empty()){
                int top = que.front(); que.pop();
                Node[top].cnt += Node[Node[top].fail].cnt;
                for(int i=0; i<Letter; i++){
                    int &v = Node[top].Next[i];
                    if(v){
                        que.push(v);
                        Node[v].fail = Node[Node[top].fail].Next[i];
                    }else v = Node[Node[top].fail].Next[i];
                }
            }
        }
    }ac;
    
    char S[44];
    int dp[11*11*11*11+5][505];
    
    int Solve()
    {
        int num[4]; memset(num, 0, sizeof(num));
        for(int i=0; S[i]; i++) num[mp[S[i]]]++;
    
        int HashCnt = 0;
        for(int A=0; A<=num[0]; A++)
            for(int T=0; T<=num[1]; T++)
                for(int G=0; G<=num[2]; G++)
                    for(int C=0; C<=num[3]; C++)
                        Hash[A][T][G][C] = HashCnt++;
    
    
            for(int j=0; j<=ac.Size; j++)
                for(int i=0; i<=HashCnt; i++)
                    dp[i][j] = -1;
    
        dp[0][0] = 0;
    
        for(int A=0; A<=num[0]; A++){
            for(int T=0; T<=num[1]; T++){
                for(int G=0; G<=num[2]; G++){
                    for(int C=0; C<=num[3]; C++){
                        for(int i=0; i<ac.Size; i++){
                            int j = Hash[A][T][G][C];
                            if(dp[j][i] >= 0){
                                for(int k=0; k<4; k++){
                                    if(k==0 && A == num[0]) continue;
                                    if(k==1 && T == num[1]) continue;
                                    if(k==2 && G == num[2]) continue;
                                    if(k==3 && C == num[3]) continue;
                                    int a, t, g, c;
                                    a = (k==0), t = (k==1);
                                    g = (k==2), c = (k==3);
                                    dp[Hash[A+a][T+t][G+g][C+c]][ac.Node[i].Next[k]]
                                    = max(dp[Hash[A+a][T+t][G+g][C+c]][ac.Node[i].Next[k]],
                                          dp[j][i] + ac.Node[ac.Node[i].Next[k]].cnt);
                                }
                            }
                        }
                    }
                }
            }
        }
    
        int ans = 0;
        int MaxNum = Hash[num[0]][num[1]][num[2]][num[3]];
        for(int i=0; i<ac.Size; i++)
            ans = max(ans, dp[MaxNum][i]);
    
        return ans;
    }
    
    int main(void)
    {
        int n, Case = 1;
    
        mp['A']=0, mp['T']=1;
        mp['G']=2, mp['C']=3;
    
        while(~scanf("%d", &n) && n){
            ac.init();
            for(int i=0; i<n; i++){
                scanf("%s", S);
                ac.insert(S);
            }ac.BuildFail();
            scanf("%s", S);
            printf("Case %d: %d
    ", Case++, Solve());
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Java进行数据库导出导入 亲测可用
    Linux 进程监控和自动重启的简单实现
    Linux的运行级别详细说明
    设置让程序开机自启动的方法
    Java读书推荐
    谈谈Python、Java与AI
    这是一篇测试随笔。
    雷林鹏分享:Apache POI数据库
    雷林鹏分享:Apache POI打印区域
    雷林鹏分享:Apache POI超链接
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7653385.html
Copyright © 2020-2023  润新知