题目描述:
N(<=50)个有效基因串,每个长度<=10,你有的基因串M,长度<=40,重排M,使得其包含的有效基因串数最多(可重叠),输出最多个数。
基因只有四个字母AGCT。
题目分析:
有效基因串建立AC自动机。
f
[
a
]
[
b
]
[
c
]
[
d
]
[
i
]
f[a][b][c][d][i]
f[a][b][c][d][i]表示分别用了
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d个字母A,G,C,T,当前在自动机的
i
i
i号点得到的最多有效基因数,枚举添加字符转移,最坏复杂度
O
(
1
1
4
∗
500
∗
4
)
O(11^4*500*4)
O(114∗500∗4)。
f f f显然不能直接开五维数组,可以枚举状态进行编号,直接dfs也是比较方便的,预处理出每个状态转移到的下一个状态的编号,五维就可以缩为 f [ 1 1 4 ] [ i ] f[11^4][i] f[114][i]。
编号也可以通过变进制,记A G C T的总数分别为 s 1 , s 2 , s 3 , s 4 s_1,s_2,s_3,s_4 s1,s2,s3,s4,那么 ( a , b , c , d ) (a,b,c,d) (a,b,c,d)就可以表示为 a + b ∗ ( s 1 + 1 ) + c ∗ ( s 1 + 1 ) ( s 2 + 1 ) + d ∗ ( s 1 + 1 ) ( s 2 + 1 ) ( s 3 + 1 ) a+b*(s_1+1)+c*(s_1+1)(s_2+1)+d*(s_1+1)(s_2+1)(s_3+1) a+b∗(s1+1)+c∗(s1+1)(s2+1)+d∗(s1+1)(s2+1)(s3+1)。最好在DP前把当前状态的下一个状态的编号存在数组里,避免在DP里面再去求下一个状态,减小常数。
Code:
#include<bits/stdc++.h>
#define maxn 505
#define maxc 4
using namespace std;
int T,n,t[4],ch[maxn][maxc],fail[maxn],cnt[maxn],tot,f[15005][maxn],nxt[15005][maxc],sum;
char s[45];
inline int Num(char c){return c=='A'?0:c=='G'?1:c=='C'?2:3;}
void Clear(){
memset(ch,0,(tot+1)*maxc*4),memset(fail,0,(tot+1)*4);
memset(cnt,0,(tot+1)*4),memset(t,0,sizeof t),tot=0;
}
void insert(char *s){
int r=0,v;
for(int i=0;s[i];i++,r=ch[r][v]) if(!ch[r][v=Num(s[i])]) ch[r][v]=++tot;
cnt[r]++;
}
int q[maxn],L,R;
void build(){
q[L=R=0]=0;
while(L<=R){
int r=q[L++],c; cnt[r]+=cnt[fail[r]];
for(int i=0;i<maxc;i++)
if(c=ch[r][i]) fail[c]=r?ch[fail[r]][i]:0,q[++R]=c;
else ch[r][i]=ch[fail[r]][i];
}
}
void solve(){
int a[4],b[4]={1,t[0]+1,(t[0]+1)*(t[1]+1),(t[0]+1)*(t[1]+1)*(t[2]+1)};
for(a[0]=0;a[0]<=t[0];a[0]++) for(a[1]=0;a[1]<=t[1];a[1]++) for(a[2]=0;a[2]<=t[2];a[2]++) for(a[3]=0;a[3]<=t[3];a[3]++){
int now=a[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];
for(int i=0;i<4;i++) nxt[now][i]=a[i]<t[i]?now+b[i]:-1;
}
sum=b[3]*(t[3]+1)-1,memset(f,-1,(sum+1)*maxn*4),f[0][0]=0;
for(int s=0;s<sum;s++) for(int i=0;i<=tot;i++) if(~f[s][i])
for(int j=0,x,c;j<4;j++) if(~(x=nxt[s][j]))
c=ch[i][j],f[x][c]=max(f[x][c],f[s][i]+cnt[c]);
printf("Case %d: %d\n",++T,*max_element(f[sum],f[sum]+tot+1));
}
int main()
{
while(scanf("%d",&n),n){
for(int i=1;i<=n;i++) scanf("%s",s),insert(s);
build();
scanf("%s",s);
for(int i=0;s[i];i++) t[Num(s[i])]++;
solve(),Clear();;
}
}