[説明] GREWords(ACオートマトン)
効果の件名:
文字列、自重の異なる位置の文字列の順序を考えます。さて、文字列の前に列が背景にある文字列の部分文字列であるように、あなたはサブシーケンスを選択してみましょう。条件を満たすようにプロモーター配列の最大重量を求めてください。配列重量はすべての要素との重量です。
単純なアプローチ設けDP、考える\(DP(i)を\)選択された文字列を表す(私は\)\を最大重み値のシーケンスにより列挙された文字列の前に直接進む\(KMP \)か否かを判断しますストリングは、その後、複雑転送される\(O(N ^ 3) \)
交流オートマトン、よく知られている、上記の練習に最適化を考えてみましょう\(\不合格)一方の鎖のルートに任意のノードのツリーは、いくつかの接尾辞です。次に、すべてのサブストリングは、自動機械のAC上の文字列であるすべてのノードのルートへのチェーンの全てです。(=自身の特定の接頭辞のサブ接尾辞)
上記最適化するには、\(DPを\) 、それはすぐにサブストリング値のすべてを持っている彼の右の列に応じて最大値を発見することが可能となる、データ構造を維持するために私たちを必要とします。、このようなアプローチを考えてみましょう\(\不合格)今私が持っていると仮定し、うち一人で木\(DPを\)彼の更新に値のみを考慮の連続期間のサブツリー取って、サブツリーに影響します(\をDFS \)系列、私たちは、撮影した直線セグメントツリーセクションを維持している\(最大\)シングルポイントデマンド\(最大\)することができます。
直接アウトに構築することができます最初にして更新に失敗回し\(DP \)を木の形状が失敗を決定するように、。
全体的に複雑\(O(N \ログN )\)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=3e5+5;
const int inf=1e9+5;
int to[maxn],w[maxn];
char c[maxn];
string sav[maxn];
namespace Graph{}
namespace ACautomaton{}
namespace SegmentTree{int n,que(const int&,const int&,const int&,const int&);}
namespace Graph{
vector<int> e[maxn];
int dfn[maxn],End[maxn],timer;
inline void add(const int&fr,const int&to){
e[fr].push_back(to);
e[to].push_back(fr);
}
void dfs(const int&now,const int&last){
dfn[now]=++timer;
for(auto t:e[now])
if(t!=last)
dfs(t,now);
End[now]=timer;
}
inline void init(){for(int t=0;t<maxn;++t) e[t].clear(); timer=0;}
}
namespace ACautomaton{
using Graph::add;
struct E{
int son[26],fail;
E(){memset(son,0,sizeof son); fail=0;}
inline int& operator [](int x){return son[x];}
inline int& operator [](char x){return son[x-'a'];}
}ac[maxn];
int cnt;
inline void add(const char*data,const int&n,const int&id){
int now=0;
for(int t=1;t<=n;++t){
if(!ac[now][data[t]]) ac[now][data[t]]=++cnt;
now=ac[now][data[t]];
}
to[id]=now;
}
queue<int> q;
inline void gen(){
queue<int>().swap(q);
for(char c='a';c<='z';++c)
if(ac[0][c])
q.push(ac[0][c]),ac[ac[0][c]].fail=0;
while(q.size()){
int now=q.front(); q.pop();
for(char c='a';c<='z';++c){
if(ac[now][c]) ac[ac[now][c]].fail=ac[ac[now].fail][c],q.push(ac[now][c]);
else ac[now][c]=ac[ac[now].fail][c];
}
}
for(int t=1;t<=cnt;++t) add(t,ac[t].fail);
}
inline void init(){memset(ac,0,sizeof ac); memset(to,0,sizeof to); memset(w,0,sizeof w); cnt=0;}
inline int getans(const string&f){
int now=0,ret=0;
for(const auto&t:f)
ret=max(ret,SegmentTree::que(Graph::dfn[now=ac[now][t]],1,SegmentTree::n,1));
return ret;
}
}
namespace SegmentTree{
#define pp(pos) seg[pos].val=max(seg[pos<<1].val,seg[pos<<1|1].val)
#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1
struct E{
int val,tag;
E(){val=tag=0;}
E(const int&a,const int&b){val=a; tag=b;}
}seg[maxn<<2];
inline void pd(const int&pos){
if(seg[pos].tag==0) return;
seg[pos<<1].val=max(seg[pos<<1].val,seg[pos].tag);
seg[pos<<1|1].val=max(seg[pos<<1|1].val,seg[pos].tag);
seg[pos<<1].tag=max(seg[pos<<1].tag,seg[pos].tag);
seg[pos<<1|1].tag=max(seg[pos<<1|1].tag,seg[pos].tag);
seg[pos].tag=0;
}
void build(const int&l,const int&r,const int&pos){
seg[pos].val=seg[pos].tag=0;
if(l==r) return;
build(lef); build(rgt);
}
void upd(const int&L,const int&R,const int&k,const int&l,const int&r,const int&pos){
if(L>r||R<l) return;
if(L<=l&&r<=R) {seg[pos].val=max(seg[pos].val,k); seg[pos].tag=max(seg[pos].tag,k); return;}
pd(pos);
upd(L,R,k,lef); upd(L,R,k,rgt);
pp(pos);
}
int que(const int&k,const int&l,const int&r,const int&pos){
if(k<l||k>r) return 0;
if(l==r) return seg[pos].val;
pd(pos);
int ret=max(que(k,lef),que(k,rgt));
pp(pos);
return ret;
}
inline void init(const int&a){n=a;build(1,n,1);}
#undef lef
#undef rgt
#undef mid
}
int main(){
int T=qr(),F=0;
while(T--){
ACautomaton::init();
Graph::init();
int n=qr();
for(int t=1;t<=n;++t){
scanf("%s",c+1);
sav[t]=c+1;
ACautomaton::add(c,strlen(c+1),t);
w[t]=qr();
}
ACautomaton::gen();
Graph::dfs(0,0);
SegmentTree::init(Graph::timer);
int ans=0;
for(int t=1;t<=n;++t){
int k=w[t]+ACautomaton::getans(sav[t]);
SegmentTree::upd(Graph::dfn[to[t]],Graph::End[to[t]],k,1,SegmentTree::n,1);
ans=max(k,ans);
}
printf("Case #%d: %d\n",++F,ans);
}
return 0;
}