潜龙未见静水流,沉默深藏待时秋。一朝破空声势振,惊世骇俗展雄猷。
随笔 - 82, 文章 - 0, 评论 - 3, 阅读 - 2153

CF1437G Death DBMS 题解

目录

题目描述

给定 n 个字符串 si ,每个字符串有一个权值,初始为 0

接下来 m 次操作:

  • 将第 k 个字符串的权值修改为 x
  • 给定一个字符串 t ,求所有是 t 的子串的字符串 si 权值最大值,如果不存在则输出 -1

数据范围

  • 1n,m3105
  • 0x109,1|s|,|t|3105

分析

重工业数据结构,想题五分钟,写题两小时。

子串相关,果断建出 AC 自动机。

对于第一类操作,注意可能有重复的字符串,用 multiset 维护 AC 自动机上每个点的权值。

对于第二类操作,把 t 放在 AC 自动机上跑。每走一个字符,查询 fail 树上当前位置在 fail 树上到根的链中权值最大值,树剖线段树即可。

时间复杂度 O((n+m+|s|+|t|)log2|s|)

天坑:常规写法中 fail 树下标从 0 开始,失配时会走到 0 号点(根节点),但是常规写法树剖初始化 son[u]=0 以便后续更新。因此本题在求 son[u] 时需要特判 son[u]=0 的情形。如果你想下标平移也不是不行,但是会多很多细节并且像博主一样浪费好多时间

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int m,n,cnt,tot;
int pos[maxn],val[maxn];
char s[maxn];
int fa[maxn],sz[maxn],son[maxn],dfn[maxn],top[maxn];
vector<int> g[maxn];
struct node
{
    int ch[26],fail;
    multiset<int> s;
}f[maxn];
void getfail()
{
    queue<int> q;
    for(int i=0;i<26;i++) if(f[0].ch[i]) q.push(f[0].ch[i]);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<26;i++)
            if(f[u].ch[i]) f[f[u].ch[i]].fail=f[f[u].fail].ch[i],q.push(f[u].ch[i]);
            else f[u].ch[i]=f[f[u].fail].ch[i];
    }
}
void dfs1(int u)
{
    sz[u]=1;
    for(auto v:g[u])
    {
        fa[v]=u,dfs1(v),sz[u]+=sz[v];
        if(!son[u]||sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int _f)
{
    dfn[u]=++cnt,top[u]=_f;
    if(son[u]) dfs2(son[u],_f);
    for(auto v:g[u]) if(v!=son[u]) dfs2(v,v);
}
namespace sgmt
{
    int p,mx[3*maxn];
    void pushup(int p)
    {
        mx[p]=max(mx[p<<1],mx[p<<1|1]);
    }
    void init()
    {
        p=1<<(__lg(cnt)+1);
        for(int i=p;i<=p+cnt+1;i++) mx[i]=-1;
        for(int i=1;i<=n;i++) mx[p+dfn[pos[i]]]=0;
        for(int i=p-1;i>=1;i--) pushup(i);
    }
    void modify(int x,int v)
    {
        x+=p,mx[x]=v;
        for(;x!=1;x>>=1) pushup(x>>1);
    }
    int query(int l,int r)
    {
        int res=-1;
        for(l=p+l-1,r=p+r+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1) res=max(res,mx[l^1]);
            if(r&1) res=max(res,mx[r^1]);
        }
        return res;
    }
}
int query(int x)
{
    int res=-1;
    while(x) res=max(res,sgmt::query(dfn[top[x]],dfn[x])),x=fa[top[x]];
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,p=0;i<=n;i++)
    {
        scanf("%s",s+1),p=0;
        for(int j=1,l=strlen(s+1);j<=l;j++)
        {
            int c=s[j]-'a';
            if(!f[p].ch[c]) f[p].ch[c]=++tot;
            p=f[p].ch[c];
        }
        pos[i]=p,f[p].s.insert(0);
    }
    getfail();
    for(int i=1;i<=tot;i++) g[f[i].fail].push_back(i);
    dfs1(0),dfs2(0,0),sgmt::init();
    for(int op=0,x=0,y=0;m--;)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d",&x,&y);
            auto &s=f[pos[x]].s;
            s.erase(s.find(val[x])),s.insert(val[x]=y);
            sgmt::modify(dfn[pos[x]],*--s.end());
        }
        else
        {
            scanf("%s",s+1),x=-1;
            for(int i=1,l=strlen(s+1),p=0;i<=l;i++) p=f[p].ch[s[i]-'a'],x=max(x,query(p));
            printf("%d\n",x);
        }
    }
    return 0;
}

posted on   peiwenjun  阅读(2)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示