AC自动机+DP, 此题有俩个优化目标:最少字母使文章符合要求,并让加成分之和尽可能高,可以把俩个目标合并成一个,即让删除一个字母所获得的加分为一个很小的值(此题可以取-200000),这样优化的目标即变为是的加分最大,最后再把结果分离即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>
#include <map>
#include <string>
#include <climits>
#include <set>
#include <string>
#include <sstream>
#include <utility>
#include <ctime>
using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::istringstream;
using std::make_pair;
using std::getline;
using std::greater;
using std::endl;
using std::multimap;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PAIR;
typedef multimap<int, int> MMAP;
const int MAXN(1610);
const int SIGMA_SIZE(26);
const int MAXM(110);
const int MAXE(4000010);
const int MAXH(18);
const int INFI((INT_MAX-1) >> 1);
const int MOD(9999991);
const ULL LIM(1000000000000000ull);
struct AC
{
int ch[MAXN][SIGMA_SIZE];
int val[MAXN], f[MAXN], score[MAXN];
int size;
void init()
{
memset(ch[0], 0, sizeof(ch[0]));
f[0] = val[0] = score[0] = 0;
size = 1;
}
inline int idx(char temp)
{
return temp-'a';
}
void insert(char *S, int tv, int ts)
{
int u = 0, id;
for(; *S; ++S)
{
id = idx(*S);
if(!ch[u][id])
{
memset(ch[size], 0, sizeof(ch[size]));
val[size] = score[size] = 0;
ch[u][id] = size++;
}
u = ch[u][id];
}
val[u] |= tv;
score[u] += ts;
}
int que[MAXN];
int front, back;
void construct()
{
front = back = 0;
int cur, u;
for(int i = 0; i < SIGMA_SIZE; ++i)
{
u = ch[0][i];
if(u)
{
que[back++] = u;
f[u] = 0;
}
}
while(front < back)
{
cur = que[front++];
for(int i = 0; i < SIGMA_SIZE; ++i)
{
u = ch[cur][i];
if(u)
{
que[back++] = u;
f[u] = ch[f[cur]][i];
val[u] |= val[f[u]];
score[u] += score[f[u]];
}
else
ch[cur][i] = ch[f[cur]][i];
}
}
}
};
AC ac;
int table[2][MAXN][1 << 9];
char str[110];
void solve(int len, int m1, int m2)
{
int cur = 0, last = 1, lim = (1 << m1)-1;
int forbid = ((1 << m2)-1)^lim;
for(int i = 0; i < ac.size; ++i)
for(int j = 0; j <= lim; ++j)
table[last][i][j] = -INFI;
table[last][0][0] = 0;
for(int i = 0; i < len; ++i)
{
for(int j = 0; j < ac.size; ++j)
for(int k = 0; k <= lim; ++k)
table[cur][j][k] = -INFI;
for(int j = 0; j < ac.size; ++j)
for(int k = 0; k <= lim; ++k)
if(table[last][j][k] != -INFI)
{
table[cur][j][k] = max(table[cur][j][k], table[last][j][k]-200000);
int ts = ac.ch[j][ac.idx(str[i])];
if((ac.val[ts]&forbid) == 0)
table[cur][ts][k|ac.val[ts]] = max(table[cur][ts][k|ac.val[ts]], table[last][j][k]+ac.score[ts]);
}
cur ^= 1;
last ^= 1;
}
int ans = -INFI;
for(int i = 0; i < ac.size; ++i)
ans = max(ans, table[last][i][lim]);
if(ans == -INFI)
printf("Banned\n");
else
{
int a1 = ans/-200000;
int a2 = ans-a1*-200000;
if(a2 <= -100000)
++a1;
a2 = ans-a1*-200000;
printf("%d %d\n", a1, a2);
}
}
char rec[110][20];
int recv[110];
int main()
{
int TC, n_case(0);
scanf("%d", &TC);
while(TC--)
{
ac.init();
int m1 = 0, m2 = 0;
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf("%s%d", rec[i], recv+i);
if(recv[i] == 999)
++m1;
}
m2 = m1;
m1 = 0;
for(int i = 0; i < n; ++i)
{
if(recv[i] == 999)
ac.insert(rec[i], 1 << m1++, 0);
else
if(recv[i] == -999)
ac.insert(rec[i], 1 << m2++, 0);
else
ac.insert(rec[i], 0, recv[i]);
}
ac.construct();
scanf("%s", str);
printf("Case %d: ", ++n_case);
solve(strlen(str), m1, m2);
}
return 0;
}