G. Death DBMS(查询每个主串和n个模板串匹配后val最大值,支持单点更新)

题:https://codeforces.com/contest/1437/problem/G

题意:首先给定n个模式串,每个模式串一开始价值为0,支持q个查询:

  • [1, x, val ]:将x位置的模式串价值改为val;
  • [2, s ] :找出在主串s中能匹配的模式串的最大值

分析:

  • 将建立n个模式串的ac自动机,然后建立fail树;
  • 因为沿fail树向下走的感觉就是找更长的“后缀”,那么到最长后缀为止,所有可能后缀都在根节点到当前点上;
  • 所以询问就是(主串在trie上跑的每个节点映射到fail树上的)节点到fail树的根路径上的信息最大值;
  • 那么更新就是fail树上单点更新;
  • 那么要高效维护查询操作,就是对树进行树剖+线段树;
  • 因为可能会存在相同模式串,在trie上同时指向一个节点,所以要处理一下相同的情况
G. Death DBMS(查询每个主串和n个模板串匹配后val最大值,支持单点更新)
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define lson root<<1,l,midd
#define rson root<<1|1,midd+1,r
const int M=3e5+5;
int tr[M<<2];
const int maxn=26;
int trie[M][maxn],fail[M],las[M],sz[M],tp[M],son[M],id[M],dfn[M],rdfn[M],fa[M];
char s[M];
vector<int>g[M];
multiset<int>mx[M];
int tot;
struct AC{
    int cnt=0;
    void Insert(char buf[],int sign){
        int len=strlen(buf),now=0;
        for(int i=0;i<len;i++){
            int x=buf[i]-'a';
            if(!trie[now][x])
                trie[now][x]=++cnt;
            now=trie[now][x];
        }
        mx[now].insert(0);
        id[sign]=now;
    }
    void getfail(){
        queue<int>que;
        while(!que.empty()) que.pop();
        for(int i=0;i<maxn;i++)
            if(trie[0][i]){
                fail[trie[0][i]]=0;
                que.push(trie[0][i]);
            }
        while(!que.empty()){
            int now=que.front();
            que.pop();
            for(int i=0;i<maxn;i++)
                if(trie[now][i]){
                    fail[trie[now][i]]=trie[fail[now]][i];
                    que.push(trie[now][i]);
                }
                else
                    trie[now][i]=trie[fail[now]][i];
        }
    }
}ac;
void dfs1(int u,int f){
    fa[u]=f,sz[u]=1;
    for(auto v:g[u]){
        if(v!=f){
            dfs1(v,u);
            sz[u]+=sz[v];
            if(!son[u]||sz[son[u]]<sz[v]) son[u]=v;
        }

    }
}
void dfs2(int u,int top){
    tp[u]=top;
    dfn[u]=++tot;
    rdfn[tot]=u;
    if(son[u])
        dfs2(son[u],top);
    for(auto v:g[u])
        if(v!=fa[u]&&v!=son[u])dfs2(v,v);
}
void up(int root){ tr[root]=max(tr[root<<1],tr[root<<1|1]); }
void build(int root,int l,int r){
    tr[root]=-1;
    if(l==r){
        tr[root]=*mx[rdfn[l]].rbegin();
        return ;
    }
    int midd=(l+r)>>1;
    build(lson);
    build(rson);
    up(root);
}
void update(int pos,int c,int root,int l,int r){
    if(l==r){
        tr[root]=c;
        return ;
    }
    int midd=(l+r)>>1;
    if(pos<=midd)
        update(pos,c,lson);
    else
        update(pos,c,rson);
    up(root);
}
int query(int L,int R,int root,int l,int r){
    if(L<=l&&r<=R){
        return tr[root];
    }
    int midd=(l+r)>>1;
    int res=-1;
    if(L<=midd) res=query(L,R,lson);
    if(R>midd) res=max(res,query(L,R,rson));
    return res;
}
int trquery(int u){
    int res=-1;
    while(tp[u]!=0){
        res=max(res,query(dfn[tp[u]],dfn[u],1,1,tot));
        u=fa[tp[u]];
    }
    res=max(res,query(dfn[0],dfn[u],1,1,tot));
    return res;
}
int solve(char buf[]){
    int len=strlen(buf),now=0;
    int ans=-1;
    for(int i=0;i<len;i++){
        now=trie[now][buf[i]-'a'];
        ans=max(ans,trquery(now));
    }
    return ans;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        ac.Insert(s,i);
    }
    ac.getfail();
    mx[0].insert(-1);
    for(int i=1;i<=ac.cnt;i++){
        g[fail[i]].pb(i);
        mx[i].insert(-1);
    }
    dfs1(0,-1);
    dfs2(0,0);
    build(1,1,tot);
    for(int i=1;i<=m;i++){
        int op;
        scanf("%d",&op);
        if(op==1){
            int x,val;
            scanf("%d%d",&x,&val);
            ///可能会出现相同的模式串,只要最大的那个
            ///更新时要记录上个更新值,用来下次更新时删除掉(也就是“覆盖”)
            mx[id[x]].erase(mx[id[x]].find(las[x]));
            las[x]=val;
            mx[id[x]].insert(las[x]);
            update(dfn[id[x]],*mx[id[x]].rbegin(),1,1,tot);
        }
        else{
            scanf("%s",s);
            printf("%d\n",solve(s));
        }
    }
    return 0;
}
View Code

 

上一篇:域渗透TIPS:获取LAPS管理员密码


下一篇:HDU 6136 Death Podracing (堆)