https://acm.hdu.edu.cn/showproblem.php?pid=4117
AC自动机每次插入都要重构,但其实可以先离线建好,再进行操作
每个子串都可以表示成一个前缀的一个后缀。
任意一个前缀是Trie树上的一个点,然后其对应后缀就是fail树上的祖先
fail树本质是一个后缀树
现在在fail树上操作,对每个点查询all祖先,可以变成祖先修改时对其所有儿子进行修改、
维护出每个点的dfs序,然后拿个线段树维护即可
#include
#include
#include
using namespace std;
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//mt19937 rand(time(0));
//mt19937_64 rand(time(0));
//srand(time(0));
#define N 20010
#define M 300010
//#define mo
int n, m, i, j, k, T, T0;
int nxt[M][26], fail[M], tot;
short dfn[M], len[M], lst[M], p[M];
int rt, u, v, w[N], ans, f;
char str[M];
int q[M], l, r;
vector<int>G[M];
int mx[N<<3];
void add(int k, int l, int r, int x, int y, int z) {
if(l>=x && r<=y) return mx[k]=max(mx[k], z), void();
int m=(l+r)>>1;
if(x<=m) add(k<<1, l, m, x, y, z);
if(y>=m+1) add(k<<1|1, m+1, r, x, y, z);
}
int que(int k, int l, int r, int x) {
if(l==r) return mx[k];
int m=(l+r)>>1;
if(x<=m) return max(mx[k], que(k<<1, l, m, x));
else return max(mx[k], que(k<<1|1, m+1, r, x));
}
void clear(int k, int l, int r) {
mx[k]=0;
if(l==r) return ;
int m=(l+r)>>1;
clear(k<<1, l, m); clear(k<<1|1, m+1, r);
}
void init() {
rt=u=v=tot=ans=l=r=0;
}
void Trie(int u, int i) {
if(!str[i]) return dfn[u]=lst[u]=1, void();
int c=str[i]-'a'; p[u]=1;
if(!nxt[u][c]) nxt[u][c]=++tot;
Trie(nxt[u][c], i+1);
}
void bfs() {
memset(fail, 0, sizeof(fail));
q[++r]=1; fail[1]=0;
while(l<r) {
u=q[++l];
if(!p[u]) continue;
for(int i=0; i<26; ++i)
if(nxt[u][i]) {
v=nxt[u][i]; k=fail[u];
while(k && p[k] && !nxt[k][i]) k=fail[k];
if(p[k] && nxt[k][i]) fail[v]=nxt[k][i];
else fail[v]=1;
q[++r]=v;
}
}
}
void dfs(int x) {
if(dfn[x]) ++m; dfn[x]=m;
for(int y : G[x]) dfs(y);
if(lst[x]) lst[x]=m, ++m;
else lst[x]=m;
}
signed main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
T=read();
for(T0=1; T0<=T; ++T0) {
init();
n=read(); rt=tot=1;
for(i=1; i<=n; ++i) {
scanf("%s", str+len[i-1]+1); w[i]=read();
len[i]=strlen(str+len[i-1]+1)+len[i-1];
Trie(rt, len[i-1]+1);
}
bfs();
for(i=2; i<=tot; ++i) G[fail[i]].pb(i);
m=1; dfs(1);
for(i=1; i<=n; ++i) {
f=w[i];
for(j=len[i-1]+1, u=1; j<=len[i]; ++j) {
u=nxt[u][str[j]-'a'];
f=max(f, que(1, 1, m, dfn[u])+w[i]);
}
ans=max(ans, f); add(1, 1, m, dfn[u], lst[u], f);
}
clear(1, 1, m);
for(i=1; i<=tot; ++i) {
if(p[i]) memset(nxt[i], 0, sizeof(nxt[i]));
fail[i]=dfn[i]=lst[i]=p[i]=0; G[i].clear();
}
printf("Case #%d: %d\n", T0, ans);
}
return 0;
}