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; }