Codeforces 1437G Death DBMS AC自动机
Codeforces 1437G Death DBMS
题意
给\(n\)个人名串\(s_i\),每个串的初始权值为\(0\),有\(m\)次询问,询问有两种:
- \(1~i~x\),将第\(i\)个串的权值改为\(x\)。
- \(2~q\),给一个字符串\(q\),问\(q\)中出现的所有人名串的权值的最大值,没出现输出\(-1\)。
\(n,m\le 3 \cdot 10^5, \sum|s_i| \le 3 \cdot 10^5,\sum|q| \le 3 \cdot 10^5,x\le 10^9\)
分析
把所有人名插入AC自动机建fail树,一种暴力的做法是对每个终止结点开个multiset记录这个结点的子串的权值,对于修改操作在multiset上修改,查询操作把查询串在AC自动机上跑,对每个结点暴跳fail取得最大值,考虑优化,暴跳fail实际上是从当前结点到根节点的这条fail链上取得最大值,那么我们对fail树进行树链剖分,并用线段树来维护区间最值即可,时间复杂度为\(O(nlog^2n)\)。另一种巧妙的做法是,我们预处理fail链的链头,用\(top[u]\)表示结点\(u\)往上跳fail遇到的第一个有子串的结点,这样我们就可以同样对每个结点不停的跳到\(top[u]\)就会只跳那些有子串的结点,因为最多有\(\sqrt{n}\)种不同长度的人名,所以每次最多跳\(\sqrt n\)次,时间复杂度为\(O(n \sqrt{n})\)。
Code1
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=3e5+10;
const int inf=1e9;
int n,m;
char s[N];
int d[N],b[N];
vector<int>g[N];
struct SegmentTree{
int tr[N<<2],n;
int top[N],sz[N],f[N],d[N],son[N],dfn[N],id[N],val[N],tot;
void bd(int l,int r,int p){
if(l==r) return tr[p]=val[id[l]],void();
int mid=l+r>>1;
bd(lson);bd(rson);
tr[p]=max(tr[p<<1],tr[p<<1|1]);
}
void up(int x,int l,int r,int p,int k){
if(l==r) return tr[p]=k,void();
int mid=l+r>>1;
if(x<=mid) up(x,lson,k);
else up(x,rson,k);
tr[p]=max(tr[p<<1],tr[p<<1|1]);
}
int qy(int dl,int dr,int l,int r,int p){
if(l==dl&&r==dr) return tr[p];
int mid=l+r>>1;
if(dr<=mid) return qy(dl,dr,lson);
else if(dl>mid) return qy(dl,dr,rson);
else return max(qy(dl,mid,lson),qy(mid+1,dr,rson));
}
void dfs(int u){
d[u]=d[f[u]]+1;
sz[u]=1;
for(int x:g[u]){
f[x]=u;
dfs(x);
sz[u]+=sz[x];
if(sz[x]>sz[son[u]]) son[u]=x;
}
}
void dfs1(int u,int t){
top[u]=t;dfn[u]=++tot;
id[tot]=u;
if(son[u]) dfs1(son[u],t);
for(int x:g[u]){
if(x==son[u]) continue;
dfs1(x,x);
}
}
void up(int i,int k){
up(dfn[i],1,n,1,k);
}
int qy(int x,int y){
int ret=-1;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);
ret=max(ret,qy(dfn[top[x]],dfn[x],1,n,1));
x=f[top[x]];
}
if(d[x]<d[y]) swap(x,y);
return max(ret,qy(dfn[y],dfn[x],1,n,1));
}
void init(int _n){
n=_n;
dfs(1);
dfs1(1,1);
bd(1,n,1);
}
}seg;
struct ACtree{
int son[N][26],fail[N],top[N],tot;
multiset<int>cnt[N];
int newnode(){
for(int i=0;i<26;i++) son[tot][i]=0;
cnt[tot++].clear();
return tot-1;
}
void init(){
tot=0;
newnode();
}
void ins(int x,char s[]){
int rt=0,m=strlen(s);
for(int i=0;i<m;i++){
if(!son[rt][s[i]-'a']) son[rt][s[i]-'a']=newnode();
rt=son[rt][s[i]-'a'];
}
cnt[rt].insert(0);
b[x]=rt;
}
void gao(){
queue<int>q;
for(int i=0;i<26;i++) if(son[0][i]) fail[son[0][i]]=0,q.push(son[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
if(!cnt[fail[u]].empty()) top[u]=fail[u];
else top[u]=top[fail[u]];
g[fail[u]+1].pb(u+1);
for(int i=0;i<26;i++){
if(son[u][i]){
fail[son[u][i]]=son[fail[u]][i];
q.push(son[u][i]);
}else son[u][i]=son[fail[u]][i];
}
}
for(int i=0;i<tot;i++) if(!cnt[i].empty()){
seg.val[i+1]=0;
}else seg.val[i+1]=-1;
seg.init(tot);
}
void change(int i,int x){
cnt[b[i]].erase(cnt[b[i]].lower_bound(d[i]));
d[i]=x;
cnt[b[i]].insert(x);
auto it=cnt[b[i]].end();
--it;
seg.up(b[i]+1,*it);
}
int qy(char s[]){
int rt=0,m=strlen(s),ans=-1;
for(int i=0;i<m;i++){
rt=son[rt][s[i]-'a'];
ans=max(ans,seg.qy(rt+1,1));
}
return ans;
}
}AC;
int main(){
scanf("%d%d",&n,&m);
AC.init();
for(int i=1;i<=n;i++){
scanf("%s",s);
AC.ins(i,s);
}
AC.gao();
while(m--){
int op,i,x;
scanf("%d",&op);
if(op==1){
scanf("%d%d",&i,&x);
AC.change(i,x);
}else{
scanf("%s",s);
printf("%d\n",AC.qy(s));
}
}
return 0;
}
Code2
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=3e5+10;
const int inf=1e9;
int n,m;
char s[N];
int d[N],b[N];
struct ACtree{
int son[N][26],fail[N],top[N],tot;
multiset<int>cnt[N];
int newnode(){
for(int i=0;i<26;i++) son[tot][i]=0;
cnt[tot++].clear();
return tot-1;
}
void init(){
tot=0;
newnode();
}
void ins(int x,char s[]){
int rt=0,m=strlen(s);
for(int i=0;i<m;i++){
if(!son[rt][s[i]-'a']) son[rt][s[i]-'a']=newnode();
rt=son[rt][s[i]-'a'];
}
cnt[rt].insert(0);
b[x]=rt;
}
void gao(){
queue<int>q;
for(int i=0;i<26;i++) if(son[0][i]) fail[son[0][i]]=0,q.push(son[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
if(!cnt[fail[u]].empty()) top[u]=fail[u];
else top[u]=top[fail[u]];
for(int i=0;i<26;i++){
if(son[u][i]){
fail[son[u][i]]=son[fail[u]][i];
q.push(son[u][i]);
}else son[u][i]=son[fail[u]][i];
}
}
}
void change(int i,int x){
cnt[b[i]].erase(cnt[b[i]].lower_bound(d[i]));
d[i]=x;
cnt[b[i]].insert(x);
}
int qy(char s[]){
int rt=0,m=strlen(s),ans=-1;
for(int i=0;i<m;i++){
rt=son[rt][s[i]-'a'];
for(int j=rt;j;j=top[j]){
if(!cnt[j].empty()){
auto it=cnt[j].end();
--it;
ans=max(ans,*it);
}
}
}
return ans;
}
}AC;
int main(){
scanf("%d%d",&n,&m);
AC.init();
for(int i=1;i<=n;i++){
scanf("%s",s);
AC.ins(i,s);
}
AC.gao();
while(m--){
int op,i,x;
scanf("%d",&op);
if(op==1){
scanf("%d%d",&i,&x);
AC.change(i,x);
}else{
scanf("%s",s);
printf("%d\n",AC.qy(s));
}
}
return 0;
}