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上同时指向一个节点,所以要处理一下相同的情况
#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; }