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

aPaPBbP
3
1 2
1 3
2 3

Sample Output

2
1
0

HINT

1<=N<=10^5

1<=M<=10^5
输入总长<=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树的利用的更多相关文章

  1. BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

    一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...

  2. 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

    [BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...

  3. BZOJ2434[Noi2011]阿狸的打字机——AC自动机+dfs序+树状数组

    题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小 ...

  4. [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组

    [NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...

  5. bzoj 2434 [Noi2011]阿狸的打字机 AC自动机

    [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4001  Solved: 2198[Submit][Status][D ...

  6. BZOJ 2434 阿狸的打字机(ac自动机+dfs序+树状数组)

    题意 给你一些串,还有一些询问 问你第x个串在第y个串中出现了多少次 思路 对这些串建ac自动机 根据fail树的性质:若x节点是trie中root到t任意一个节点的fail树的祖先,那么x一定是y的 ...

  7. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  8. BZOJ2434:[NOI2011]阿狸的打字机(AC自动机,线段树)

    Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的 ...

  9. BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)

    Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4140  Solved: 2276[Submit][Status][Discuss] Descript ...

随机推荐

  1. dos2unix命令详解

    基础命令学习目录首页 原文链接:https://blog.csdn.net/leedaning/article/details/53024290 使用git 的时候碰到git将unix换行符转换为wi ...

  2. Daily Srum 10.30

    Android那一组打算用SQL Server这个关系型数据库,而王鹿鸣他们一组却是依赖于Hbase,这是一件很麻烦的事,所以我打算在这两方面都建立一个数据库.虽然挺麻烦,但是还是为了扩展性所做的必要 ...

  3. caffe添加自己的数据输入层

    整体思路: 阅读caffe数据输入层各个类之间的继承关系,确定当前类需要继承的父类以及所需参数的设置. 编写zzq_data.cpp 在layer_factory.cpp中完成注册: 在caffe.p ...

  4. Android笔记-5-EditText密码和Checkbox二选一

    EditText密码:明文和密文 密文: public class MainActivity extends Activity { private EditText password = null; ...

  5. NABCD(校园包车)

    广州商学院包车 N(need) 各个高校包车需求量大,然而校园内包车信息太散乱,售票地点不确定,有些老师.学生特别是新生,甚至不知有校园包车这一回事, 随着信息网络的发展,为了给师生带来校园更多的方便 ...

  6. 索引超出了数组界限。 在 System.Collections.Generic.Dictionary`2.Resize

    博问:Dictionary 超出了数组界限 异常: Exception type: IndexOutOfRangeException Exception message: 索引超出了数组界限. 在 S ...

  7. java属性编辑器,即PropertyEditor

    出处:http://www.iteye.com/topic/1123628

  8. 《软件工程和Python》PYTHON效能分析和Django

    资料汇总网站:http://www.yzhiliao.com/my/course/55 一..作业下面两个题目任选一题: (1)运用jieba库分词(或者你喜欢的其他库),并把代码发到git上去(不发 ...

  9. Matlab图像匹配问题

    已知一个任意形状,查找在大图像中最接近的形状位置. 输入:一个小图形状和一张大图 输出:最接近的形状在大图中的位置 假设: (1)已知形状与目标形状有一定的形变. (2)形状与大图像均为二值图像,图中 ...

  10. week3c:个人博客作业

    程序测试: 一个基本的测试. 在Visual Studio 2013 中使用C++单元测试 操作如下: 这是我学到的过程. 有复杂程序的测试.以后有时间再弄.