题目大意:
给你一个只含"AGCT"四种字符的长度为 n( n ≤ 40 n \leq 40 n≤40)的字符串S.然后给m( m ≤ 50 m \leq 50 m≤50)个长度小于等于10的匹配串。你现在可以重新排列S使得这m个匹配串在其中出现的次数最多.输出这个次数.
题目思路:
建AC自动机后
其实不难想到一个朴素的转移方程: d p ( a 0 , a 1 , a 2 , a 3 , p o s ) dp(a_0,a_1,a_2,a_3,pos) dp(a0,a1,a2,a3,pos)代表字符串S还剩下 a 0 a_0 a0个A , a 1 a_1 a1个G , a 2 a_2 a2个C , a 3 a_3 a3个T 且当前在Trie图中的节点为 p o s pos pos的出现最多次数。
但是问题是A,G,C,T都可能出现40次。这里我就蠢了。因为 A+G+C+T=40.所以根据均值不等式,状态数最多为: 1 0 4 = 10000 10^4 = 10000 104=10000 种。而不是 4 0 4 40^4 404了. 这样我们就必须给状态编号.然后直接刷表法dp即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500 + 5;
int ha[42][42][42][42] , num[10] , cnt , dp[14641][505] , bk[1005];
struct S
{
int a[4];
}sta[14641];
namespace AC{
int tr[maxn][5] , tot;
int fail[maxn] , v[maxn];
void init ()
{
tot = 0;
for (int i = 0 ; i < maxn ; i++){
for (int j = 0 ; j < 4 ; j++) tr[i][j] = 0;
fail[i] = v[i] = 0;
}
}
void add (char * s){
int u = 0;
for (int i = 1 ; s[i] ; i++){
if (!tr[u][bk[s[i]]]) tr[u][bk[s[i]]] = ++tot;
u = tr[u][bk[s[i]]];
}
v[u]++;
}
queue<int>q;
void build (){
while (q.size()) q.pop();
for (int i = 0 ; i < 4 ; i++) if (tr[0][i]) q.push(tr[0][i]);
while (q.size()){
int u = q.front();q.pop();
for (int i = 0 ; i < 4 ; i++){
if (tr[u][i]){
fail[tr[u][i]] = tr[fail[u]][i];
q.push(tr[u][i]);
}else {
tr[u][i] = tr[fail[u]][i];
}
}
v[u] += v[fail[u]];
}
}
}
char tmp[15] , s[1005];
int main()
{
int n;
bk['A'] = 0;
bk['G'] = 1;
bk['C'] = 2;
bk['T'] = 3;
int t = 0;
while ( scanf("%d" , &n) , n ){
AC::init();
for (int i = 1 ; i <= n ; i++){
scanf("%s" , tmp + 1);
AC::add(tmp);
}
AC::build();
scanf("%s" , s + 1);
memset(num , 0 , sizeof num);
for (int i = 1 ; s[i] ; i++) num[bk[s[i]]]++;
cnt = 0;
for (int i = 0 ; i <= num[0] ; i++)
for (int j = 0 ; j <= num[1] ; j++)
for (int k = 0 ; k <= num[2] ; k++)
for (int l = 0 ; l <= num[3] ; l++){
ha[i][j][k][l] = cnt++ ,
sta[cnt - 1].a[0] = i , sta[cnt - 1].a[1] = j , sta[cnt - 1].a[2] = k , sta[cnt - 1].a[3] = l;
for (int gg = 0 ; gg <= AC::tot ; gg++) dp[cnt - 1][gg] = -1;
}
dp[ha[num[0]][num[1]][num[2]][num[3]]][0] = 0;
for (int i = cnt - 1 ; i > 0 ; i--){
for (int j = 0 ; j <= AC::tot ; j++){
if (dp[i][j] == -1) continue;
for (int k = 0 ; k < 4 ; k++){
if (sta[i].a[k] == 0) continue;
sta[i].a[k]--;
int ni = ha[sta[i].a[0]][sta[i].a[1]][sta[i].a[2]][sta[i].a[3]] , nj = AC::tr[j][k];
dp[ni][nj] = max (dp[ni][nj] , dp[i][j] + AC::v[nj]);
sta[i].a[k]++;
}
}
}
int ans = 0;
for (int i = 0 ; i <= AC::tot ; i++) if (dp[0][i] != -1) ans = max (ans , dp[0][i]);
printf("Case %d: %d\n" , ++t , ans);
}
return 0;
}