[NOI2011]阿狸的打字机——AC自动机之fail树的利用
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
Sample Input
3
1 2
1 3
2 3
Sample Output
1
0
HINT
1<=N<=10^5
Solution
是一道能让我们深刻理解AC自动机的好题。
输入很有意思。不是直接给n个字符串,而是给了一个打字的序列。
发现,如果暴力找所有的字符串的话,那么会n^2 爆炸
B,P有点意思。而且还是可以累积的。
所以,可以直接建出来一棵trie树。B就是返回father,打一个字母就是找一个son,P就是在这个点打标记。
怎么找?
暴力kmp匹配n^2
我们有多个模式串,肯定要想一想AC自动机
简化问题,假设一个主串T,多次询问一个S在T中出现次数?
trie毕竟是一个前缀集合,而S在T的出现位置,只能是若干个T[1...i]前缀的后缀。
如果我们走一遍T,把路径上点权++,代表这个点是一个T的前缀。
怎么判断S在某个前缀中出现过?
反过来,如果一个前缀T[1...i]中有S,那么从i不断跳fail指针,必然有一次会跳到S的结尾处。
而,AC自动机的fail是一棵树,如果按照fail的方向,是一棵内向树。
在fail树中,如果i是S结尾处的子树中的节点,那么意味着i跳fail可以到S结尾处。
所以,可以用一个树状数组维护dfn序,就可以查询S结尾处子树内权值和。就是出现次数。
如果主串T稍微动一下(末尾添加字符,删除末尾字符),也可以做到。直接把点权加上、删去即可。
而这个主串变动其实是在trie上dfs
每次dfs移动显然不划算。
所以离线询问,挂在所有T的末尾。
如果dfs到Ti的末尾,回答所有询问。即Sj的fail树子树的权值和。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
int n,m;
int sum;
struct node{
int sub,key;
int ans;
}que[N];
//trie&&AC machine
int ch[N][];
bool has[N][];
int tot;
int fail[N];
int fa[N];
vector<int>mem[N];
vector<int>exi[N];
int on[N];
char s[N];
void pre(){
int now=;
fa[]=;
for(int i=;i<=n;i++){
if(s[i]=='P'){
sum++;
on[sum]=now;
exi[now].push_back(sum);
}
else if(s[i]=='B'){
now=fa[now];
}
else{
int x=s[i]-'a';
if(!ch[now][x]) has[now][x]=,ch[now][x]=++tot,fa[ch[now][x]]=now;
now=ch[now][x];
}
}
}
//fail tree
struct edge{
int nxt,to;
}e[*N];
int hd[N],cnt;
void add(int x,int y){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
}
int dfn[N],dfn2[N];
int df;
void dfs(int x,int fa){
dfn[x]=++df;
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa)continue;
dfs(y,x);
}
dfn2[x]=df;
}
void build(){
queue<int>q;
for(int i=;i<=;i++){
if(has[][i]) fail[ch[][i]]=,add(,ch[][i]),q.push(ch[][i]);
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=;i<=;i++){
if(has[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];
add(ch[fail[x]][i],ch[x][i]);
q.push(ch[x][i]);
}
else{
ch[x][i]=ch[fail[x]][i];
}
}
}
}
//array tree
int f[N];
void upda(int x,int c){
for(;x<=n;x+=x&(-x)) f[x]+=c;
}
int query(int x){
int ret=;for(;x;x-=x&(-x)) ret+=f[x];return ret;
}
//ans
void sol(int x,int fa){
upda(dfn[x],);
for(int i=;i<mem[x].size();i++){
int son=on[que[mem[x][i]].sub];
int kk=query(dfn2[son])-query(dfn[son]-);
que[mem[x][i]].ans=kk;
}
for(int i=;i<=;i++){
if(has[x][i]){
sol(ch[x][i],x);
}
}
upda(dfn[x],-);
}
int main(){
scanf("%s",s+);
n=strlen(s+);
pre();
build();
dfs(,);//warning root is 0
scanf("%d",&m);
int x,y;
for(int i=;i<=m;i++){
scanf("%d%d",&que[i].sub,&que[i].key);
que[i].ans=;
mem[on[que[i].key]].push_back(i);
}
sol(,);
for(int i=;i<=m;i++){
printf("%d\n",que[i].ans);
}
return ;
}
upda:2019.3.27:
直接暴力fail树上线段树合并,就可以在线辣
[NOI2011]阿狸的打字机——AC自动机之fail树的利用的更多相关文章
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...
- 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组
[BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...
- BZOJ2434[Noi2011]阿狸的打字机——AC自动机+dfs序+树状数组
题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小 ...
- [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组
[NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...
- bzoj 2434 [Noi2011]阿狸的打字机 AC自动机
[Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 4001 Solved: 2198[Submit][Status][D ...
- BZOJ 2434 阿狸的打字机(ac自动机+dfs序+树状数组)
题意 给你一些串,还有一些询问 问你第x个串在第y个串中出现了多少次 思路 对这些串建ac自动机 根据fail树的性质:若x节点是trie中root到t任意一个节点的fail树的祖先,那么x一定是y的 ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ2434:[NOI2011]阿狸的打字机(AC自动机,线段树)
Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的 ...
- BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 4140 Solved: 2276[Submit][Status][Discuss] Descript ...
随机推荐
- smash:一个类unix内核
前言 每一个蹩脚的C++程序员都有一颗做操作系统内核的心.我从大学毕业开始就对操作系统内核感兴趣,将其看作是术之尽头,可惜那时候一直在无忧无虑的忙着玩网游,也就搁置了.随着时间的推移,逐渐就将其淡忘了 ...
- NO.4:自学python之路------内置方法、装饰器、迭代器
引言 是时候开始新的Python学习了,最近要考英语,可能不会周更,但是尽量吧. 正文 内置方法 Python提供给了使用者很多内置方法,可以便于编程使用.这里就来挑选其中大部分的内置方法进行解释其用 ...
- Tree - Decision Tree with sklearn source code
After talking about Information theory, now let's come to one of its application - Decision Tree! No ...
- Binary Tree的3种非Recursive遍历
Binary Tree Preorder Traversal Given a binary tree, return the preorder traversal of its nodes' valu ...
- PSP总结
一.回顾1 (1)回想一下你曾经对计算机专业的畅想 当初你是如何做出选择计算机专业的决定的?经过一个学期,你的看法改变了么,为什么? 以前的回答:当初之所以选择计算机专业,是因为我比较喜欢数学,觉得计 ...
- 2017秋-软件工程第十二次作业(一)-PSP总结
[回顾]:回顾开学时的博客并回答相关问题 1.回想一下你曾经对计算机专业的畅想当初你是如何做出选择计算机专业的决定的?经过一个学期,你的看法改变了么,为什么?答:当初的决定是以前的事情,没有改变.经历 ...
- iOS软件"一天八杯水“app开发过程
作为一个ios系统测试者和app外观设计者.我们首先要了解iOS系统的开发工具和资源.xcode和iOS sdk作为一个免费的开发环境值得我们去学习和了解.interface builder提供创建了 ...
- 对cnblogs.com的用户体验
1.你是什么样的用户, 有什么样的心理, 对cnblogs 的期望值是什么? 我们是计算机专业学生,是奔向神奇的代码世界的旅人.希望在cnblogs上找到自己感兴趣的技术,并学到更多的知识,提升自己的 ...
- url传多值问题
使用url传值的特点是操作简单,虽然安全性低,但依然广泛运用. url传数据绑定的值: <a href='Default.aspx?id=<%#Eval("ID")%& ...
- 校友聊NABCD需求分析
校友聊 NABCD需求分析 N:内网用户流量不够使用 A:基于局域网进行通讯 B:通讯不花费外网流量 C:目前学校还没有使用 D:将软件放在校园网,可以下载使用