Lost's revenge

题目大意:先给你一些子串,然后给你一个母串,母串里面的字母可以任意调换位置,问最多这个母串经过一些位置变动最多能包含多少个子串。
 
分析:可以比较明显的看出来的DP,先求出来ATGC分别有多少,然后再处理,不过有个比较麻烦的地方,因为ATGC的字母数最多是40,因为不知道那种字母所以是40*40*40*40,很明显这种复杂度太高,开始使用了一次打标,把每种状态都保存下来,并且保存它的下一个状态,不过很不幸这种方法TLE了,因为找他的下一个状态不是太容易,看了大神的题解后明白,其实可以使用4种不同的进制来做,进制数就是这个碱基数的总个数,这样复杂度很明显就降低了,而且状态再转移的时候也很容易。
 
代码如下:
===================================================================================================================================
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

const int MAXN = 15007;
const int MaxSon = 4;
const int oo = 1e9;

int Find(char ch)
{
    if(ch == 'A')return 0;
    if(ch == 'T')return 1;
    if(ch == 'G')return 2;

    return 3;
}
struct Ac_Trie
{
    int next[507][4];
    int Fail[507], End[507];
    int root, cnt;

    int newnode()
    {
        for(int i=0; i<MaxSon; i++)
            next[cnt][i] = -1;
        Fail[cnt] = End[cnt] = false;

        return cnt++;
    }
    void InIt()
    {
        cnt = 0;
        root = newnode();
    }
    void Insert(char s[])
    {
        int now = root;

        for(int i=0; s[i]; i++)
        {
            int k = Find(s[i]);

            if(next[now][k] == -1)
                next[now][k] = newnode();
            now = next[now][k];
        }

        End[now] += 1 ;
    }
    void GetFail()
    {
        int now = root;
        queue<int> Q;

        for(int i=0; i<MaxSon; i++)
        {
            if(next[now][i] == -1)
                next[now][i] = root;
            else
            {
                Fail[next[now][i]] = root;
                Q.push(next[now][i]);
            }
        }

        while(Q.size())
        {
            now = Q.front();
            Q.pop();

            for(int i=0; i<MaxSon; 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]);
                }
            }

            End[now] += End[Fail[now]];
        }
    }
};
struct ATGC
{
    int sum[4], bit[4];
    int dp[507][MAXN];
    Ac_Trie ac;

    void GetSum(char s[])
    {
        memset(sum, 0, sizeof(sum));
        memset(bit, 0, sizeof(bit));

        for(int i=0; s[i]; i++)
        {
            int k = Find(s[i]);
            sum[k]++;
        }
    }
    void GetBit()
    {
        bit[0] = (sum[1]+1)*(sum[2]+1)*(sum[3]+1);
        bit[1] = (sum[2]+1)*(sum[3]+1);
        bit[2] = sum[3]+1;
        bit[3] = 1;
    }
    int GetDp()
    {
        memset(dp, -1, sizeof(dp));
        dp[0][0] = 0;

        int ans = 0;

        for(int A=0; A<=sum[0]; A++)
        for(int T=0; T<=sum[1]; T++)
        for(int G=0; G<=sum[2]; G++)
        for(int C=0; C<=sum[3]; C++)
        {
            int k = A*bit[0] + T*bit[1] + G*bit[2] + C*bit[3];

            for(int i=0; i<ac.cnt; i++)
            {
                if(dp[i][k] == -1)continue;

                for(int j=0; j<MaxSon; j++)
                {
                    if(j == 0 && A==sum[0])continue;
                    if(j == 1 && T==sum[1])continue;
                    if(j == 2 && G==sum[2])continue;
                    if(j == 3 && C==sum[3])continue;

                    int next = ac.next[i][j];
                    int nextk = k + bit[j];

                    dp[next][nextk] = max(dp[next][nextk], dp[i][k]+ac.End[next]);
                    ans = max(ans, dp[next][nextk]);
                }
            }
        }

        return ans;
    }
};

ATGC a;

int main()
{
    int i, N, t=1;

    while(scanf("%d", &N), N)
    {
        char s[107];
        a.ac.InIt();

        for(i=0; i<N; i++)
        {
            scanf("%s", s);
            a.ac.Insert(s);
        }
        a.ac.GetFail();

        scanf("%s", s);

        a.GetSum(s);
        a.GetBit();

        int ans = a.GetDp();

        printf("Case %d: %d
", t++, ans);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/liuxin13/p/4772879.html