题意:
给定n个模式串,和一个匹配串,
现在你可以对匹配串重新排序,问最多能包含多少个模式串(可以重复累计)。
保证模式串和匹配串只出现ACGT四种字符。
数据范围:n<=50,每个模式串长度不超过10,匹配串长度不超过40.
解法:
统计出匹配串每种字符的数量
令d[i][a][c][g][t][x]表示前i步,acgt字符使用状态,当前位于节点x,的最大值
发下acgt的数量=i,因此步数的那一维可以去掉.
令d[a][c][g][t][x]为acgt使用状态,当前位于节点x,的最大值.
但是acgt的最大数量为40,开40^4的空间显然不可能.
考虑到acgt的总和最大为40,有许多状态是不需要的,
如果每个字符10个,那么每种字符当前使用数量的范围在[0,10],有11种,
总状态数为11^4<15000,所以将状态hash一下,
令id[i][j][k][l]为此时acgt数量分别为的i,j,k,l时的hash值,
那么只需要令d[i][j]表示acgt使用状态为i,当前位于节点j的最大值就行了.
然后在ac自动机上dp即可.
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
char s[maxm];
int id[45][45][45][45],idx;
int cnt[4];
int n;
void init_hash(){
memset(id,0,sizeof id);
idx=0;
for(int i=0;i<=cnt[0];i++){
for(int j=0;j<=cnt[1];j++){
for(int x=0;x<=cnt[2];x++){
for(int y=0;y<=cnt[3];y++){
id[i][j][x][y]=++idx;
}
}
}
}
}
int get(char c){
if(c=='A')return 0;
if(c=='C')return 1;
if(c=='G')return 2;
return 3;
}
struct AC{
int a[maxm][4];
int fail[maxm];
int val[maxm];
int tot=0;
void init(){
for(int i=0;i<=tot;i++){
memset(a[i],0,sizeof a[i]);
fail[i]=val[i]=0;
}
tot=0;
}
void add(char *s){
int len=strlen(s);
int node=0;
for(int i=0;i<len;i++){
int v=get(s[i]);
if(!a[node][v])a[node][v]=++tot;
node=a[node][v];
}
val[node]++;
}
void build(){
queue<int>q;
for(int i=0;i<4;i++){
if(a[0][i]){
q.push(a[0][i]);
fail[a[0][i]]=0;
}
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<4;i++){
if(a[x][i]){
fail[a[x][i]]=a[fail[x]][i];
val[a[x][i]]+=val[a[fail[x]][i]];//因为可以重叠,所以要累加
q.push(a[x][i]);
}else{
a[x][i]=a[fail[x]][i];
}
}
}
}
//
int d[maxm][15000+5];//d[i][j]表示当前在i节点,字符使用状态为j的最大值.
int solve(){
//init
for(int i=0;i<=tot;i++){
for(int j=1;j<=idx;j++){
d[i][j]=-1;
}
}
//dp
d[0][1]=0;
for(int i=0;i<=cnt[0];i++){
for(int j=0;j<=cnt[1];j++){
for(int k=0;k<=cnt[2];k++){
for(int l=0;l<=cnt[3];l++){
int now=id[i][j][k][l];
for(int x=0;x<=tot;x++){//枚举当前节点
if(d[x][now]==-1)continue;
if(i!=cnt[0]){//加一个A
int to=id[i+1][j][k][l];
int v=a[x][0];
d[v][to]=max(d[v][to],d[x][now]+val[v]);
}
if(j!=cnt[1]){//加一个C
int to=id[i][j+1][k][l];
int v=a[x][1];
d[v][to]=max(d[v][to],d[x][now]+val[v]);
}
if(k!=cnt[2]){//加一个G
int to=id[i][j][k+1][l];
int v=a[x][2];
d[v][to]=max(d[v][to],d[x][now]+val[v]);
}
if(l!=cnt[3]){//加一个T
int to=id[i][j][k][l+1];
int v=a[x][3];
d[v][to]=max(d[v][to],d[x][now]+val[v]);
}
}
}
}
}
}
//
int ans=0;
for(int i=0;i<=tot;i++){
ans=max(ans,d[i][idx]);
}
return ans;
}
}ac;
signed main(){
int cas=1;
while(scanf("%d",&n)!=EOF&&n){
ac.init();
for(int i=1;i<=n;i++){
scanf("%s",s);
ac.add(s);
}
ac.build();
//
scanf("%s",s);
int len=strlen(s);
for(int i=0;i<4;i++)cnt[i]=0;
for(int i=0;i<len;i++){
if(s[i]=='A')s[i]=0;
else if(s[i]=='C')s[i]=1;
else if(s[i]=='G')s[i]=2;
else if(s[i]=='T')s[i]=3;
cnt[(int)s[i]]++;
}
init_hash();
int ans=ac.solve();
printf("Case %d: ",cas++);
printf("%d\n",ans);
}
return 0;
}