#include <cstdio> #include <cstring> #include <deque> #define max(a,b) ((a) > (b) ? (a) : (b)) #define min(a,b) ((a) < (b) ? (a) : (b)) using namespace std; const int LMT = 102; const int SIZE = 656; const int STAT = 256; const int inf = 10000000; const int wiz = 26; int del[2][SIZE][STAT], val[2][SIZE][STAT], siz, word; int gra[SIZE][wiz], fail[SIZE]; /********** 搞清楚要求的是什么啊。。。 这里最重要的是del。 前状态其实已经可以从tire树中取得,不用再确定当前字符所接的前一个字符的位置。 这种题目直接YY成图中的遍历似乎会好很多。 偷看了cxlove神的代码,羞愧啊... 这种题里其实有两种递推关系,一种是tire树中遍历,以当前所在节点为状态还有一种是寻找当前字母在字符串中的前字符,大神果断选了第一种,可是我居然把两种都选了,以至于想不出啊 **********/ struct __node { bool can; int point, state; }node[SIZE]; void init(void) { int i, j, k; siz = 1; word = 0; memset(gra, 0, sizeof(gra)); memset(fail, 0, sizeof(fail)); for(i = 0; i< 2; ++i) for(j = 0; j < SIZE; ++j) for(k =0; k < STAT; ++k) { del[i][j][k] = inf; val[i][j][k] = -inf; } for(i = 0; i < SIZE; ++i) { node[i].point = node[i].state = 0; node[i].can = 1; } } void insert(char sec[],const int &__point) { int i, current, index; for(current = 0, i = 0; sec[i]; ++i) { index = sec[i] - 'a'; if (!gra[current][index]) gra[current][index] = siz++; current = gra[current][index]; } if(__point != 999 && __point != -999) { node[current].can = 1; node[current].point += __point; } else if(__point == 999) { node[current].can = 1; node[current].state |= 1 << (word++); } else node[current].can = 0; } void build_ac(void) { int current,v; deque<int>q; q.clear(); for(int i=0;i<wiz;i++) if(gra[0][i])q.push_back(gra[0][i]); while(!q.empty()) { current=q.front(); q.pop_front(); for(int i = 0;i < wiz;++i) if(gra[current][i]) { v = gra[current][i]; fail[v] = gra[fail[current]][i]; node[v].can &= node[fail[v]].can; node[v].point += node[fail[v]].point; node[v].state |= node[fail[v]].state; q.push_back(v); } else gra[current][i] = gra[fail[current]][i]; } } int main(void) { char str[LMT]; int T, n, g,lim, i, ans, value, current, index, state, I = 1; scanf("%d", &T); while(T--) { init(); lim = 0;ans = inf; scanf("%d",&n); while(n--) { scanf("%s%d", str, &g); insert(str,g); } build_ac(); lim = (1 << word) -1; str[0] = 5; scanf("%s", &str[1]); del[0][0][0] = 0; val[0][0][0] = 0; for(i = 1;str[i]; ++i) { for(current = 0; current < siz; ++current) for(state = 0; state <= lim; ++state) { del[i & 1][current][state] = del[(i + 1) & 1][current][state] + 1;//删除当前字符,节点不变 val[i & 1][current][state] = val[(i + 1) & 1][current][state]; } for(current = 0; current < siz; ++current) for(state = 0; state <=lim; ++state) { index = gra[current][str[i] - 'a']; if (!node[index].can || del[(i + 1) & 1][current][state] >= inf)continue; if (del[i & 1][index][state | node[index].state] > del[(i + 1) & 1][current][state]) { del[i & 1][index][state | node[index].state] = del[(i + 1) & 1][current][state]; val[i & 1][index][state | node[index].state] = val[(i + 1) & 1][current][state] + node[index].point; } else if (del[i & 1][index][state | node[index].state] == del[(i + 1) & 1][current][state] && val[i & 1][index][state | node[index].state] < val[(i + 1) & 1][current][state] + node[index].point ) val[i & 1][index][state | node[index].state] = val[(i + 1) & 1][current][state] + node[index].point; } } for(current = 0; current < siz; ++current) if (ans > del[(i + 1) & 1][current][lim]) { ans = del[(i + 1) & 1][current][lim]; value = val[(i + 1) & 1][current][lim]; } else if (ans == del[(i + 1) & 1][current][lim] && value < val[(i + 1) & 1][current][lim]) value = val[(i + 1) & 1][current][lim]; printf("Case %d: ", I++); if (ans >= inf) printf("Banned\n"); else printf("%d %d\n", ans, value); } return 0; }