题意:给出n个子串,和一个母串。问用母串中的字符最多可以组合出几个子串。可以重叠
最裸的是用dp[i][a][b][c][d]表示走到i号节点,用了a个'A' b个'G' c个'C' d个'T',但是这样开不下,用状态压缩,把a,b,c,d压缩成一维,因为总数只有40个,所以压缩后,状态不会很大,开个15555就可以过了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std ;
const int maxn = 555 ;
int dp[maxn][22222] ;
int len , cnt[4] , g[4] ;
const int inf = 11111111 ;
int T ;
int get_k ( char v )
{
int k ;
switch ( v )
{
case 'A' : k = 0 ; break ;
case 'G' : k = 1 ; break ;
case 'C' : k = 2 ; break ;
case 'T' : k = 3 ; break ;
}
return k ;
}
void init ()
{
T = 1 ;
int i , j , k , l ;
for ( i = 0 ; i < 4 ; i ++ )
{
T *= ( cnt[i] + 1 ) ;
}
g[0] = 1 ;
for ( i = 1 ; i < 4 ; i ++ ) g[i] = g[i-1] * ( cnt[i-1] + 1 ) ;
}
class AC_auto
{
private :
int tot ;
int c[4][maxn] ;
int id[maxn] ;
int fail[maxn] ;
queue<int> q ;
public :
int new_node ()
{
int i ;
id[tot] = 0 ;
fail[tot] = 0 ;
for ( i = 0 ; i < 4 ; i ++ ) c[i][tot] = 0 ;
return tot ++ ;
}
void init () { while ( !q.empty () ) q.pop () ; tot = 0 ; new_node () ; }
void insert ( char *s )
{
int now = 0 ;
for ( ; *s ; s ++ )
{
int k = get_k ( *s ) ;
if ( !c[k][now] ) c[k][now] = new_node () ;
now = c[k][now] ;
}
id[now] ++ ;
}
void get_fail ()
{
int i , j , u = 0 ;
for ( i = 0 ; i < 4 ; i ++ ) if ( c[i][u] ) q.push ( c[i][u] ) ;
while ( !q.empty () )
{
u = q.front () ;
q.pop () ;
for ( i = 0 ; i < 4 ; i ++ )
{
if ( !c[i][u] )
{
c[i][u] = c[i][fail[u]] ;
continue ;
}
int e = c[i][u] ;
j = fail[u] ;
fail[e] = c[i][j] ;
id[e] += id[fail[e]] ;
q.push ( e ) ;
}
}
}
void work ()
{
int i , j , k , l , t , f ;
for ( i = 0 ; i <= tot ; i ++ )
for ( j = 0 ; j <= T ; j ++ )
dp[i][j] = -inf ;
dp[0][0] = 0 ;
for ( i = 0 ; i < T ; i ++ )
{
for ( t = 0 ; t < tot ; t ++ )
{
if ( dp[t][i] == -inf ) continue ;
int u = i ;
for ( f = 0 ; f < 4 ; f ++ )
{
int p = u % ( cnt[f] + 1 ) ;
u = u / ( cnt[f] + 1 ) ;
if ( p == cnt[f] ) continue ;
int e = i + g[f] ;
int v = c[f][t] ;
dp[v][e] = max ( dp[v][e] , dp[t][i] + id[v] ) ;
}
}
}
int ans = 0 ;
for ( i = 0 ; i < tot ; i ++ )
ans = max ( ans , dp[i][T-1] ) ;
printf ( "%d\n" , ans ) ;
}
} ac ;
char s[55] ;
int main ()
{
int n , i , j ;
int cas = 0 ;
while ( scanf ( "%d" , &n ) != EOF )
{
if ( n == 0 ) break ;
cas ++ ;
ac.init () ;
for ( i = 1 ; i <= n ; i ++ )
{
scanf ( "%s" , s ) ;
ac.insert ( s ) ;
}
ac.get_fail () ;
for ( i = 0 ; i < 4 ; i ++ ) cnt[i] = 0 ;
scanf ( "%s" , s ) ;
len = strlen ( s ) ;
for ( i = 0 ; i < len ; i ++ )
{
int k = get_k ( s[i] ) ;
cnt[k] ++ ;
}
init () ;
printf ( "Case %d: " , cas ) ;
ac.work () ;
}
}
/*
5
AATCATTGCT
TATC
TCCAG
CT
TATCCATGG
TTAAAGAAAT
0
2
ATCCGG
CCGTCAGGG
GATGGAATGTGAACTGACTAGTTTGTTTCCCCC
3
*/