首页 > 其他分享 >Luogu P2414 NOI2011 阿狸的打字机 题解 [ 紫 ] [ AC 自动机 ] [ 离线思想 ] [ 树状数组 ] [ dfs 序 ]

Luogu P2414 NOI2011 阿狸的打字机 题解 [ 紫 ] [ AC 自动机 ] [ 离线思想 ] [ 树状数组 ] [ dfs 序 ]

时间:2025-01-07 23:44:19浏览次数:1  
标签:AC 匹配 int 题解 离线 100005 自动机

阿狸的打字机:非常牛的 AC 自动机题。

暴力

先考虑在暴力的情况下,我们如何计算 \(x\) 匹配 \(y\) 的次数。显然,我们会模拟往 \(y\) 里加字符的过程,在此过程中做 KMP 进行匹配,统计答案。

那么如果涉及多个模式串呢?就可以把 KMP 加强成 AC 自动机了。

考虑在 AC 自动机上如何刻画这个过程,在匹配过程中,我们从到达过的每一个点出发,往前暴力跳 fail 树,找出匹配 \(x\) 的后缀即可。

转化

从 \(y\) 的路线去找 \(x\) 非常的没有前途,我们考虑从 \(x\) 出发,能不能找到 \(y\) 的路线。题意转化为求 \(x\) 的子树与 \(y\) 到根链的交集包含的节点数。

\(x\) 能匹配上的串一定是它在 fail 树中子树的所有节点,这个“子树”就让人很容易想到用 dfs 序转化为序列上的区间问题来解决。那么我们如何处理 \(y\) 的路线呢?

观察

这个就涉及到本题很巧妙的一个性质了,本题给出的打印字符串实际上是可以动态维护的,我们考虑维护一个栈,里面存 AC 自动机上的指针,回退的时候 pop 即可。

在这个过程中,我们可以不断给新到达的节点的权值进行 \(+1,-1\) 的操作,这个操作一共会进行大概 \(2\times (n-1)\) 次,它是由 fail 树的边数决定的。

那么接下来就是很简单的事情了,我们按 \(y\) 的大小将询问排序并离线下来,在动态维护字符串的过程中标记 \(y\) 到根节点的链,这个可以用树状数组或者线段树来进行这个单点修改,区间查询的操作。查询的时候就查子树的区间即可。

时间复杂度 \(O(n \log n)\)。

代码

代码比较长,但是如果分模块完成的话应该不会很难实现。

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
vector<pi>qs[100005];
int n,m,p=0,ch[100005][30],ap[100005],idx=0,cnt=0,ne[100005],now=0;
int lx[100005],rx[100005],tr[100005],ans[100005];
char s[100005];
stack<int>stk;
vector<int>g[100005];
void build()
{
    queue<int>q;
    for(int i=0;i<26;i++)
    {
        if(ch[0][i])q.push(ch[0][i]);
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            int v=ch[u][i];
            if(v)ne[v]=ch[ne[u]][i],q.push(v);
            else ch[u][i]=ch[ne[u]][i];
        }
    }
}
void init()
{
    for(int i=1;i<=idx;i++)g[ne[i]].push_back(i);
}
void dfs(int u)
{
    lx[u]=++now;
    for(auto v:g[u])dfs(v);
    rx[u]=now;
}
int lowbit(int x){return (x&(-x));}
int query(int x)
{
    if(x<=0)return 0;
    int ans=0;
    while(x)
    {
        ans+=tr[x];
        x-=lowbit(x);
    }
    return ans;
}
void update(int x,int v)
{
    if(x<=0)x=1;
    while(x<=now)
    {
        tr[x]+=v;
        x+=lowbit(x);
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>s+1;
    n=strlen(s+1);
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        qs[y].push_back({x,i});
    }
    stk.push(0);
    p=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]>='a'&&s[i]<='z')
        {
            int c=s[i]-'a';
            if(ch[p][c]==0)ch[p][c]=++idx;
            p=ch[p][c];
            stk.push(p);
        }
        else if(s[i]=='B')
        {
            stk.pop();
            p=stk.top();
        }
        else
        {
            ap[++cnt]=p;
        }
    }
    build();
    init();
    dfs(0);
    p=0;
    while(!stk.empty())stk.pop();
    stk.push(0);
    cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]>='a'&&s[i]<='z')
        {
            int c=s[i]-'a';
            p=ch[p][c];
            stk.push(p);
            update(lx[p],1);
        }
        else if(s[i]=='B')
        {
            update(lx[p],-1);
            stk.pop();
            p=stk.top();
        }
        else
        {
            cnt++;
            for(auto que:qs[cnt])
            {
                int x=que.fi,id=que.se;
                ans[id]=query(rx[ap[x]])-query(lx[ap[x]]-1);
            }
        }
    }    
    for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
    return 0;
}

标签:AC,匹配,int,题解,离线,100005,自动机
From: https://www.cnblogs.com/zhr0102/p/18658674

相关文章

  • Go 语言与 Tesseract OCR 实现英文数字验证码识别
    Go语言本身不直接支持图像识别,但可以通过调用TesseractOCR引擎来进行图像识别。我们可以使用Go的tesseract包来实现这一功能。一、安装与配置安装TesseractOCR首先,你需要在系统中安装TesseractOCR。安装方法和前面一样:Ubuntu(Linux):bashsudoapt-getupdatesudo......
  • 安卓笔记4——Result API 在两个Activity之间传递数据 kotlin版本
    第一个Activity//接收第二个Activity返回的回调privatevalrequestDataLauncher=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result->if(result.resultCode==RESULT_OK){valdata=result.data?.getS......
  • CF2057E2 Another Exercise on Graphs (hard version) 题解
    感觉和[NOI2018]归程有点像(?考虑对每个询问二分答案,设二分到的答案是\(x\),要判断路径上的\(k\)大值是否能不大于\(x\),只需先将价值不大于\(x\)的所有边的边权设为\(0\),其他边设为\(1\),跑一遍\(a\)到\(b\)的最短路,看最短路长度是否不大于\(k\)即可。因为\(x\)的......
  • Vuex mapMutations和mapActions
    一、mapMutations1、作用:帮助我们生成与mutations对话的方法,即包含$store.commit()2、步骤a、引入import{mapActions,mapMutations}from'vuex'b、语法methods:{//mapMutations生成addNumsubisNum对象方法...mapMutations({addNum:"ADD",subisNum:"SU......
  • SecureCRT v9.5.2 for Mac SSH终端操作工具 安装
    SecureCRTv9.5.2forMacSSH终端操作工具安装SecureCRTMac破解版是一款SSH终端工具,为计算专业人士提供高级会话管理工具。也是一个功能强大且值得信赖的基于GUI的SHH和Telnet客户端,以及旨在提高工作效率并简化重复任务的终端仿真器。借助SecureCRTmac版的帮助,您可以通过对AN......
  • SecureFX for Mac FTP/SSH传输工具
    SecureFXforMacFTP/SSH传输工具SecureFXmac破解版是一款Mac平台的FTP/SSH传输工具。SecureFXforMac支持三种文件传输协议:FTP、SFTP和FTPoverSSH2。它可以提供安全文件传输。无论您连接的是任何一种操作系统的服务器,它都能提供安全的传输服务。它主要用于Linux操作系统......
  • Luogu P3041 USACO12JAN Video Game G 题解 [ 紫 ] [ AC 自动机 ] [ 动态规划 ]
    VideoGamesG:弱智紫题,30min切了,dp思路非常板。多模式串一看肯定就是要建出AC自动机,然后在fail树里下传标记,预处理每个节点到达后的得分。然后设计\(dp_{i,j}\)表示第\(i\)个字符,AC自动机里匹配节点在\(j\)的最大答案,刷表法转移即可:\[dp_{i+1,ch_{j,c}}\gets\ma......
  • Apache Guacamole 部署安装(使用mysql数据库版)
    ApacheGuacamole是一个基于HTML5的远程桌面网关,支持VNC、RDP和SSH等标准协议。 一.官方链接1.官方文档https://guacamole.apache.org/doc/gug/guacamole-architecture.html  2.项目位置:https://guacamole.apache.org/https://gitcode.com/gh_mirrors/gua/gu......
  • pd虚拟机 [po] Parallels Desktop 20 激活 for Mac [jie] 安装教程【支持M芯片】
    pd虚拟机[po]ParallelsDesktop20激活forMac[jie]安装教程【支持M芯片】ParallelsDesktop20,是一款Mac虚拟机软件,在搭载AppleM系列芯片的任何Mac上运行Windows,体验不同操作系统之间无缝集成。使用ParallelsDesktop20forMac体验macOS和Windows的双重最优......
  • VMware Fusion Pro 13 for Mac虚拟机软件
    VMwareFusionPro13forMac虚拟机软件VMwareFusionProforMac,是一款mac虚拟机软件,跟ParallelsDesktop一样,都可以让你的Mac同时运行一个或多个不同的操作系统。VMwareFusionPromac不仅能让你在Mac苹果电脑上运行Windows或Linux系统、使用非Mac平台的软件,而且还可以支......