Description
给出N个优良的基因段,每段长度小于等于10,只含有AGCT四种碱基。
现给一段基因片段S,|S|<=40。对其重排列后,最多能含有多少个优良基因,基因段可以有公共部分
Input
多组用例,每组用例第一行为一个整数N表示模式串个数,最后一行为文本串S,以N=0结束输入
Output
对于每组用例,输出对文本串重排列后最多能含有多少优良基因
Sample Input
3
AC
CG
GT
CGAT
1
AA
AAA
0
Sample Output
Case 1: 3
Case 2: 2
Solution
AC自动机,字符最长是40,只需要记录ACGT出现的次数。如果使用5维数组,显然超内存了。假设ACGT的总数分别为num[0],num[1],num[2],num[3]
那么对于ACGT的数量分别为a,b,c,d的状态可以记录为:a*(num[1]+1)(num[2]+1)(num[3]+1) + b*(num[2]+1)(num[3]+1)+ c(num[3]+1) +d,这样的状态最大就是11*11*11*11,复杂度也可以承受了。状态转移方程dp[next[i][k]][state+bit[k]]=max(dp[next[i][k]][state+bit[k]],dp[i][state]+end[next[i][k]])
Code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 55
#define maxl 11
struct Trie
{
int next[maxn*maxl][4],fail[maxn*maxl],end[maxn*maxl];
int root,L;
int inv(char c)//解码
{
if(c=='A')return 0;
if(c=='C')return 1;
if(c=='G')return 2;
if(c=='T')return 3;
}
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();
}
void insert(char buf[])
{
int len=strlen(buf);
int now=root;
for(int i=0;i<len;i++)
{
if(next[now][inv(buf[i])]==-1)
next[now][inv(buf[i])]=newnode();
now=next[now][inv(buf[i])];
}
end[now]++;
}
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];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
int dp[maxn*maxl][11*11*11*11+11],bit[4],num[4];
int solve(char buf[])
{
memset(num,0,sizeof(num));//初始化
int len=strlen(buf);
for(int i=0;i<len;i++)//统计文本串中AGCT的个数
num[inv(buf[i])]++;
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;
memset(dp,-1,sizeof(dp));//初始化
dp[root][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 state=a*bit[0]+b*bit[1]+c*bit[2]+d*bit[3];//状态压缩
for(int i=0;i<L;i++)//枚举所有节点
if(dp[i][state]>=0)
for(int k=0;k<4;k++)
{
if(k==0&&a==num[0]||k==1&&b==num[1]||k==2&&c==num[2]||k==3&&d==num[3])
continue;
dp[next[i][k]][state+bit[k]]=max(dp[next[i][k]][state+bit[k]],dp[i][state]+end[next[i][k]]);
}
}
int ans=0;
int s=num[0]*bit[0]+num[1]*bit[1]+num[2]*bit[2]+num[3]*bit[3];
for(int i=0;i<L;i++)//找到含优秀基因片段数最多的串
ans=max(ans,dp[i][s]);
return ans;
}
};
char buf[55];
Trie ac;
int main()
{
int n;
int res=1;
while(scanf("%d",&n),n)
{
ac.init();//初始化
for(int i=0;i<n;i++)
{
scanf("%s",buf);
ac.insert(buf);//插入模式串
}
ac.build();//建树
scanf("%s",buf);
printf("Case %d: %d\n",res++,ac.solve(buf));
}
return 0;
}