Luogu P2414 NOI2011 阿狸的打字机 题解 [ 紫 ] [ AC 自动机 ] [ 离线思想 ] [ 树状数组 ] [ dfs 序 ]
阿狸的打字机:非常牛的 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;
}
Luogu P2414 NOI2011 阿狸的打字机 题解 [ 紫 ] [ AC 自动机 ] [ 离线思想 ] [ 树状数组 ] [ dfs 序 ]的更多相关文章
- 【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序
[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写 ...
- 【AC自动机】【树状数组】【dfs序】洛谷 P2414 [NOI2011]阿狸的打字机 题解
这一题是对AC自动机的充分理解和树dfs序的巧妙运用. 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和' ...
- BZOJ 2434 Luogu P2414 [NOI2011]阿狸的打字机 (AC自动机、树状数组)
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2434 题解: 我写的是离线做法,不知道有没有在线做法. 转化一波题意,\(x\)在AC ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- luogu SP8093 后缀自动机+树状数组+dfs序
这题解法很多,简单说几个: 1. 线段树合并,时间复杂度是 $O(nlog^2n)$ 的. 2. 暴力跳 $fail,$ 时间复杂度 $O(n\sqrt n),$ 比较暴力. 3. 建立后缀树后在 $ ...
- BZOJ.2434.[NOI2011]阿狸的打字机(AC自动机 树状数组 DFS序)
题目链接 首先不需要存储每个字符串,可以将所有输入的字符依次存进Trie树,对于每个'P',记录该串结束的位置在哪,以及当前节点对应的是第几个串(当前串即根节点到当前节点):对于'B',只需向上跳一个 ...
- [luogu P3787][新创无际夏日公开赛] 冰精冻西瓜 [树状数组][dfs序]
题目背景 盛夏,冰之妖精琪露诺发现了一大片西瓜地,终于可以吃到美味的冻西瓜啦. 题目描述 琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地.这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有 ...
- P2414 [NOI2011]阿狸的打字机
P2414 [NOI2011]阿狸的打字机 AC自动机+树状数组 优质题解 <------题目分析 先AC自动机搞出Trie图 然后根据fail指针建一只新树 把树映射(拍扁)到一个序列上,用树 ...
- 洛谷 P2414 [NOI2011]阿狸的打字机 解题报告
P2414 [NOI2011]阿狸的打字机 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母 ...
- BZOJ2434:[NOI2011]阿狸的打字机——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=2434 https://www.luogu.org/problemnew/show/P2414 打字 ...
随机推荐
- Linux之buffer/cache
buffers和cached解释 =========================================================缓存(cached)是把读取过的数据保存起来,重新读 ...
- AtCoder Beginner Contest 295
Three Days Ago 我们定义一个只由数字构成的字符串中的字符能够被重排成相同的两份,我们称这个字符串是个好字符串,比如12341234 现在给定一个字符串\(S\),找出所有的\([l,r] ...
- 文件上传漏洞&靶场通关详解
文件上传漏洞&靶场通关详解 什么是文件上传漏洞? 大部分网站都拥有上传文件的部分,文件上传漏洞是由于网站开发者对用户上传文件的过滤不够严格,攻击者可以通过这些漏洞上传可执行文件(如木马,恶意脚 ...
- Java 动态设置 JVM 参数的方法
Java虚拟机(JVM)在运行Java应用时,其性能调优和资源管理至关重要.虽然许多JVM参数在启动时通过命令行设置,但在应用运行期间动态调整某些参数也是可行的.通过动态设置JVM参数,开发者可以更有 ...
- CentOS 更换国内源
前言 centos默认为官方yum源,国内使用的下载速度完全随缘,且不稳定,一般都需要更换国内源 过程 了解版本 通过cat /etc/redhat-release 可以获取当前centos的版本 知 ...
- 在Python工具箱中,创建对应子工具集
目录 问题描述 实现方法 问题描述 在Pro中,新建自定义工具箱后,直接通过操作可以添加工具集. 但是新建python工具箱后,却没有新建的操作.因为python工具箱的对象定义,都是在脚本中定义的, ...
- docker containner挂掉,无法exec进入bash,如何修改文件的终极解决方法
场景: Nginx在bash里面配置的时候挂掉了,然后docker start不起来,exec bash进不去,造成无法再改里面的文件了 解决方法: 1,docker ps –a 可以查到所有dock ...
- 2024-12-18:正方形中的最多点数。用go语言,给定一个二维数组 points 和一个字符串 s,其中 points[i] 表示第 i 个点的坐标,s[i] 表示第 i 个点的标签。 如果一个正
2024-12-18:正方形中的最多点数.用go语言,给定一个二维数组 points 和一个字符串 s,其中 points[i] 表示第 i 个点的坐标,s[i] 表示第 i 个点的标签. 如果一个正 ...
- Vue-Router 面试题 (2023-09-13更新)
路由导航守卫 和 Vue 实例生成周期钩子函数的执行顺序? 路由导航守卫 都是在 Vue 实例生命周期钩子函数 之前执行的 Vue-Router 有哪几种导航钩子? 1. 全局守卫 全局前置守卫:be ...
- 使用 MOLECULE 迅速包装百度 UEditor
UEditor: UEditor - 首页http://ueditor.baidu.com/website/ 我们在对话框上放了几个 UEditor,发现第一次弹出对话框时UEditor还没有初始化 ...