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

Lost‘s revenge

Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 4548    Accepted Submission(s): 1274

Problem Description

Lost
and AekdyCoin are friends. They always play "number game"(A boring game
based on number theory) together. We all know that AekdyCoin is the man
called "nuclear weapon of FZU,descendant of Jingrun", because of his
talent in the field of number theory. So Lost had never won the game. He
was so ashamed and angry, but he didn‘t know how to improve his level
of number theory.

One noon, when Lost was lying on the bed, the
Spring Brother poster on the wall(Lost is a believer of Spring Brother)
said hello to him! Spring Brother said, "I‘m Spring Brother, and I saw
AekdyCoin shames you again and again. I can‘t bear my believers were
being bullied. Now, I give you a chance to rearrange your gene sequences
to defeat AekdyCoin!".

It‘s soooo crazy and unbelievable to
rearrange the gene sequences, but Lost has no choice. He knows some
genes called "number theory gene" will affect one "level of number
theory". And two of the same kind of gene in different position in the
gene sequences will affect two "level of number theory", even though
they overlap each other. There is nothing but revenge in his mind. So he
needs you help to calculate the most "level of number theory" after
rearrangement.

Input

There are less than 30 testcases.
For each testcase, first line is number of "number theory gene"

N(1<=N<=50). N=0 denotes the end of the input file.
Next N lines means the "number theory gene", and the length of every "number theory gene" is no more than 10.
The last line is Lost‘s gene sequences, its length is also less or equal 40.
All genes and gene sequences are only contains capital letter ACGT.

Output

For
each testcase, output the case number(start with 1) and the most "level
of number theory" with format like the sample output.

Sample Input

3
AC
CG
GT
CGAT
1
AA
AAA
0

Sample Output

Case 1: 3
Case 2: 2

分析:通过题意,位置可以重置,也就是最后的字符串的AGCT数量和原字符串数量相同.

由题,我们可以想到用AC自动机来记录这些“数论基因”

直观的看的话,用DP来构造,就有41*41*41*41*500种状态,很明显这样是超出内存的

但实际上A+G+C+T<=40 很多状况是不存在的,所以我们就可以进行状态压缩

设 num0=A的数量;num1=G;num2=C;num3=T

这样总共的状态就有(num0+1)*(num1+1)*(num2+1)*(num3+1)*500种,在数量最大的情况下也就是

11*11*11*11*500;

根据进制的特点,我们在列举不重复的情况的时候,可以这样把当前的状态这样化为十进制数

S=A*(num1+1)*(num2+1)*(num3+1)+G*(num2+1)*(num3+1)+C*(num3+1)+T*1;

dp[S][j]也就是当前的状态,j是在自动机上的节点位置

代码如下:

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
int num[4];
int bit[4];
int dp[11*11*11*11+10][550];
int maxx,end_len;
struct Trie
{
    int Next[550][4];//4是这里讨论4个小写字母的情况,根据情况修改
    int fail[550],end[550];//end数组表示以该节点结尾的字符串的数量
    int root,L;//L用来标记节点序号,以广度优先展开的字典树的序号
    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();
    }
    int get_id(char ch)
    {
        if(ch==‘A‘)return 0;
        else if(ch==‘G‘)return 1;
        else if(ch==‘C‘)return 2;
        else if(ch==‘T‘)return 3;
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for(int i = 0;i < len;i++)
        {
            if(Next[now][get_id(buf[i])] == -1)  //如果未建立当前的后继节点,建立新的节点
                Next[now][get_id(buf[i])] = newnode();
            now = Next[now][get_id(buf[i])];
        }
        end[now]++;//以该节点结尾的字符串数量增加1
    }
    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];//该段的最后一个节点匹配后,跳到拥有最大公共后缀的fail节点继续匹配
                else
                {
                    fail[Next[now][i]]=Next[fail[now]][i];//当前节点的fail节点等于它前驱节点的fail节点的后继节点
                    Q.push(Next[now][i]);
                }
        }
    }
    void solve()
    {
        maxx=0;
      int maxlen=bit[0]*(num[0]+1);
      for(int i=0;i<=maxlen+1;i++)
        for(int j=0;j<L;j++)
        dp[i][j]=INF;
        dp[0][0]=0;
        for(int A=0;A<=num[0];A++)
          for(int G=0;G<=num[1];G++)
           for(int C=0;C<=num[2];C++)
             for(int T=0;T<=num[3];T++)
        {
            int s=A*bit[0]+G*bit[1]+C*bit[2]+T*bit[3];
              for(int j=0;j<L;j++)
              {
                 if(dp[s][j]<INF-5)
                 {
                    for(int k=0;k<4;k++)
                    {
                      if(k==0&&A==num[0])continue;
                     else if(k==1&&G==num[1])continue;
                     else if(k==2&&C==num[2])continue;
                     else if(k==3&&T==num[3])continue;
                     if(dp[s+bit[k]][Next[j][k]]==INF)
                     dp[s+bit[k]][Next[j][k]]=dp[s][j]+end[Next[j][k]];
                     else
                     dp[s+bit[k]][Next[j][k]]=max(dp[s+bit[k]][Next[j][k]],dp[s][j]+end[Next[j][k]]);
                    }
                 }
              }
        }
      int  max_len=num[0]*bit[0]+num[1]*bit[1]+num[2]*bit[2]+num[3]*bit[3];
        for(int i=0;i<L;i++){
            if(dp[max_len][i]<INF-5)
        maxx=max(dp[max_len][i],maxx);
        }
        cout<<maxx<<endl;
    }
};
Trie ac;
char buf[110];
int main()
{
    int  t,n,ans,len,Case=0;
    while(scanf("%d",&n)!=EOF)
    {
        Case++;
        if(n==0)break;
        memset(num,0,sizeof(num));
      ac.init();
      while(n--)
     {
      scanf("%s",buf);
      ac.insert(buf);
     }
     ac.build();
     scanf("%s",buf);
     len=strlen(buf);
     for(int i=0;i<len;i++)
     {
         if(buf[i]==‘A‘)
         num[0]++;
         else if(buf[i]==‘G‘)
         num[1]++;
         else if(buf[i]==‘C‘)
         num[2]++;
         else if(buf[i]==‘T‘)
         num[3]++;
     }
     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;
     printf("Case %d: ",Case);
     ac.solve();
    }
    return 0;
}

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

时间: 2024-10-08 14:58:48

HDU 3341 Lost's revenge(AC自动机+状压DP)的相关文章

HDU 3341 Lost&#39;s revenge AC自动机+ 状态压缩DP

题意:这个题目和HDU2457有点类似,都是AC自动机上的状态dp,题意就是给你只含有'A','T','C','G',四个字符的子串和文本串,问你文本串如何排列才可以使得文本串中包含有更多的模式串 解题思路:我们知道了 有 num[0] 个 'A', num[1] 个 ‘T’, num[2] 个 ‘C’,num[3] 个‘G’, 我们的可以知道暴力的思路就是把所有的文本串都枚举出来然后一一匹配.我们膜拜了一下春哥以后,就可以有以下思路:  把一个串的信息压缩一下,把具有同样个数字符的串看成是同一

hdu 4057 Rescue the Rabbit(AC自动机+状压dp)

题目链接:hdu 4057 Rescue the Rabbit 题意: 给出一些模式串,每个串有一定的价值,现在构造一个长度为M的串,问最大的价值为多少,每个模式串最多统计一次. 题解: 由于每个模式串最多统计一次,所以我们要考虑记录是否已经存在该串. 考虑dp[i][j][k]表示当前考虑到i的长度,存在的串的组合为j,在AC自动机上走到了k这个节点的状态. 然后转移一下就能将所有能到达的状态走到.然后取一个最大值就行了. 1 #include<bits/stdc++.h> 2 #defin

hdu 3341 Lost&#39;s revenge(AC自动机+变进制状压DP)

题目链接:hdu 3341 Lost's revenge 题目大意:给定一些需要匹配的串,然后在给定一个目标串,现在可以通过交换目标串中任意两个位置的字符,要求最 后生成的串匹配尽量多的匹配串,可以重复匹配. 解题思路:这题很明显是AC自动机+DP,但是dp的状态需要开40?40?40?40(记录每种字符的个数),空间承受 不了,但是其实因为目标串的长度有限,为40:所以状态更本不需要那么多,最多只有10?10?10?10,但是通过 40进制的hash转换肯定是不行,可以根据目标串中4种字符的个

HDU 3341 Lost&#39;s revenge AC自动机+dp

Lost's revenge Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 3757    Accepted Submission(s): 1020 Problem Description Lost and AekdyCoin are friends. They always play "number game"(A bor

Hdu 3341 Lost&#39;s revenge (ac自动机+dp+hash)

题目大意: 给出很多个DNA串,每一个串的价值为1,最后给出一个长串,要你重新排列最后的串使之它所有的子串的权值和最大. 思路分析: 最先容易想到的思路就是搜!管她3721..直接一个字符一个字符的码,然后在AC自动机上判断最后的权值.TLE哟. 然后发现搜过不去,那就dp咯.再容易想到的就是dp[i][a][b][c][d] 表示此时遍历AC自动机的节点在i,然后构成了a个A,b个G,c个C,d个T的权值. 再一看内存,500*40*40*40*40...然后...就没有然后了 再想,因为它说

hdu 2825 aC自动机+状压dp

Wireless Password Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5640    Accepted Submission(s): 1785 Problem Description Liyuan lives in a old apartment. One day, he suddenly found that there

【HDU3341】 Lost&#39;s revenge (AC自动机+状压DP)

Lost's revenge Time Limit: 5000MS Memory Limit: 65535KB 64bit IO Format: %I64d & %I64u Description Lost and AekdyCoin are friends. They always play "number game"(A boring game based on number theory) together. We all know that AekdyCoin is t

poj 1699 Best Sequence(AC自动机+状压DP)

题目链接:poj 1699 Best Sequence 题目大意:给定N个DNA序列,问说最少多长的字符串包含所有序列. 解题思路:AC自动机+状压DP,先对字符串构造AC自动机,然后在dp[s][i]表示匹配了s,移动到节点i时候的最短步数. #include <cstdio> #include <cstring> #include <queue> #include <vector> #include <iostream> #include &

hdu 2825 Wireless Password(AC自动机+状压DP)

题目链接:hdu 2825 Wireless Password 题目大意:N,M,K,M个字符串作为关键码集合,现在要求长度为N,包含K个以上的关键码的字符串有多少个. 解题思路:AC自动机+dp,滚动数组,因为关键码个数不会超过10个,所以我们用二进制数表示匹配的状态.dp[i][j][k] 表示到第i个位置,j节点,匹配k个字符串. #include <cstdio> #include <cstring> #include <queue> #include <