3 AC CG GT CGAT 1 AA AAA 0
Case 1: 3 Case 2: 2
给一个串,包含ACGT,重新排序后最多包含上面多少个串,可重叠。
既然可以重新排序,那么就只跟ACGT出现的次数有关了,可以想到开个5维数组进行DP,dp[41][41][41][41][50*10],前4维分别表示每个字母个数,最后一维表示当前节点。但是这个会超内存的,注意那个串最长40,你每维都开40肯定是有浪费的。
类似于康托展开那种编码,用num[0],num[1],num[2],num[3]表示A,C,G,T的总个数,base表示基数,base[0]=(num[1]+1)*(num[2]+1)*(num[3]+1),base[1]=(num[2]+1)*(num[3]+1),base[2]=num3+1,base[3]=1,那么对于不超过总个数的任何ACGT的状态都有一一对应的编码,不会冲突。
这个问题解决了就容易了,转移方程不难。
#include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<iostream> #include<queue> using namespace std; typedef unsigned long long ULL; const int MAXN=52; const int MAXM=12; const int MAXL=45; const int MAXNODE=MAXN*MAXM; const int LOGMAXN=50; const int INF=0x3f3f3f3f; const int SIGMA_SIZE=4; const int MOD=20090717; int T,N; int dp[MAXNODE][14700]; int num[5],base[5]; struct AC{ int ch[MAXNODE][SIGMA_SIZE]; int val[MAXNODE]; int f[MAXNODE]; int sz; void clear(){ memset(ch[0],0,sizeof(ch[0])); val[0]=0; sz=1; } int idx(char c){ switch(c){ case 'A':return 0; case 'C':return 1; case 'G':return 2; case 'T':return 3; } } void insert(char* s,int v){ int u=0; for(int i=0;s[i];i++){ int c=idx(s[i]); if(!ch[u][c]){ memset(ch[sz],0,sizeof(ch[sz])); val[sz]=0; ch[u][c]=sz++; } u=ch[u][c]; } val[u]+=v; } void get_fail(){ queue<int> q; f[0]=0; for(int c=0;c<SIGMA_SIZE;c++){ int u=ch[0][c]; if(u){ f[u]=0; q.push(u); } } while(!q.empty()){ int r=q.front(); q.pop(); for(int c=0;c<SIGMA_SIZE;c++){ int u=ch[r][c]; if(!u){ ch[r][c]=ch[f[r]][c]; continue; } q.push(u); f[u]=ch[f[r]][c]; val[u]+=val[f[u]]; } } } }ac; void DP(int len){ memset(dp,-1,sizeof(dp)); dp[0][0]=0; for(int l=0;l<len;l++) for(int i=0;i<=num[0]&&i<=l;i++) for(int j=0;j<=num[1]&&j+i<=l;j++) for(int k=0;k<=num[2]&&j+i+k<=l;k++){ int q=l-i-j-k; if(q>num[3]) continue; int n=i*base[0]+j*base[1]+k*base[2]+q*base[3]; for(int u=0;u<ac.sz;u++) if(dp[u][n]!=-1){ for(int c=0;c<SIGMA_SIZE;c++){ int p; if(c==0) p=i+1; else if(c==1) p=j+1; else if(c==2) p=k+1; else p=q+1; if(p<=num[c]) dp[ac.ch[u][c]][n+base[c]]=max(dp[ac.ch[u][c]][n+base[c]],dp[u][n]+ac.val[ac.ch[u][c]]); } } } int ans=0; int m=num[0]*base[0]+num[1]*base[1]+num[2]*base[2]+num[3]*base[3]; for(int u=0;u<ac.sz;u++) ans=max(ans,dp[u][m]); printf("%d\n",ans); } char str[MAXM]; char P[MAXL]; int main(){ int cas=0; while(scanf("%d",&N)!=EOF&&N){ ac.clear(); for(int i=0;i<N;i++){ scanf("%s",str); ac.insert(str,1); } ac.get_fail(); memset(num,0,sizeof(num)); scanf("%s",P); for(int i=0;P[i];i++) num[ac.idx(P[i])]++; base[3]=1; for(int i=2;i>=0;i--) base[i]=base[i+1]*(num[i+1]+1); printf("Case %d: ",++cas); DP(strlen(P)); } return 0; }