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