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;
}
posted @ 2020-10-29 15:20  xyq0220  阅读(106)  评论(0编辑  收藏  举报