http:
#include<iostream>
#include<queue>
#include<algorithm>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
using namespace std ;
const int MAXN=400005 ;
int li[MAXN],num[MAXN];
int le[MAXN],ri[MAXN];
int col[MAXN<<2 ],mx[MAXN<<2 ],to[MAXN];
int dp[MAXN];
struct Edge
{
int t;
int next;
};
Edge edge[MAXN<<1 ];
int head[MAXN],T,cnt;
void new_edge(int a,int b)
{
edge[T].t=b;
edge[T].next=head[a];
head[a]=T++;
}
inline void push_up(int rt)
{
mx[rt]=max(mx[rt<<1 ],mx[rt<<1 |1 ]);
}
inline void push_down(int rt)
{
if (col[rt])
{
col[rt<<1 ]=max(col[rt<<1 ],col[rt]);
col[rt<<1 |1 ]=max(col[rt<<1 |1 ],col[rt]);
mx[rt<<1 ]=max(mx[rt<<1 ],col[rt]);
mx[rt<<1 |1 ]=max(mx[rt<<1 |1 ],col[rt]);
col[rt]=0 ;
}
}
void build(int l,int r,int rt)
{
col[rt]=mx[rt]=0 ;
if (l==r) return ;
int m=(l+r)>>1 ;
build(l,m,rt<<1 );
build(m+1 ,r,rt<<1 |1 );
push_up(rt);
}
void update(int a,int b,int c,int l,int r,int rt)
{
if (a<=l&&r<=b)
{
col[rt]=max(c,col[rt]);
mx[rt]=max(c,mx[rt]);
return ;
}
push_down(rt);
int m=(l+r)>>1 ;
if (a<=m)
update(a,b,c,l,m,rt<<1 );
if (m<b)
update(a,b,c,m+1 ,r,rt<<1 |1 );
push_up(rt);
}
int query(int a,int l,int r,int rt)
{
if (l==r)
return mx[rt];
push_down(rt);
int m=(l+r)>>1 ;
int ret=0 ;
if (a<=m)
ret=query(a,l,m,rt<<1 );
else
ret=query(a,m+1 ,r,rt<<1 |1 );
push_up(rt);
return ret;
}
struct AC_Auto
{
queue <int > q;
int tot;
int c[MAXN][26 ],fail[MAXN],val[MAXN];
inline int new_node()
{
for (int i=0 ; i<26 ; i++)
c[tot][i]=0 ;
fail[tot]=val[tot]=0 ;
return tot++;
}
inline int index(char c)
{
return c-'a' ;
}
void insert(char *s,int id)
{
int len=strlen (s);
int now=0 ;
for (int i=0 ; i<len; i++)
{
int k=index(s[i]);
if (!c[now][k]) c[now][k]=new_node();
now=c[now][k];
}
to[id]=now;
}
void get_fail()
{
int u=0 ;
for (int i=0 ; i<26 ; i++) if (c[u][i]) q.push(c[u][i]);
while (!q.empty())
{
u=q.front();
q.pop();
for (int i=0 ; i<26 ; i++)
{
if (c[u][i])
{
int e=c[u][i];
int j=fail[u];
fail[e]=c[j][i];
q.push(e);
}
else c[u][i]=c[fail[u]][i];
}
}
}
void init()
{
tot=0 ;
new_node();
}
void dfs(int u,int fa)
{
le[u]=++cnt;
for (int i=head[u]; i!=-1 ; i=edge[i].next)
{
int v=edge[i].t;
if (v==fa) continue ;
dfs(v,u);
}
ri[u]=cnt;
}
void connect()
{
cnt=0 ;
memset (head,-1 ,sizeof (head));
for (int i=1 ; i<tot; i++)
{
new_edge(fail[i],i);
new_edge(i,fail[i]);
}
dfs(0 ,-1 );
}
void work(char *s,int t)
{
int now=0 ;
int ans=0 ,mx=0 ;
build(1 ,cnt,1 );
for (int i=0 ; i<t; i++)
{
int k=index(s[i]);
now=c[now][k];
mx=max(mx,query(le[now],1 ,cnt,1 ));
if (li[i])
{
int add=num[li[i]]>0 ?num[li[i]]:0 ;
ans=max(ans,mx+add);
update(le[now],ri[now],mx+add,1 ,cnt,1 );
now=mx=0 ;
}
}
printf ("%d\n" ,ans);
}
};
AC_Auto ac;
char s1[MAXN];
char s2[MAXN];
int main()
{
int C,n,cases=0 ;
scanf ("%d" ,&C);
while (C--)
{
scanf ("%d" ,&n);
ac.init();
int t=0 ;
T=0 ;
memset (li,0 ,sizeof (li));
for (int i=1 ; i<=n; i++)
{
scanf ("%s%d" ,s1,&num[i]);
ac.insert(s1,i);
int len=strlen (s1);
for (int j=0 ; j<len; j++)
s2[t++]=s1[j];
li[t-1 ]=i;
}
ac.get_fail();
ac.connect();
printf ("Case #%d: " ,++cases);
ac.work(s2,t);
}
return 0 ;
}