2434: [Noi2011]阿狸的打字机

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 1834  Solved: 1053
[Submit][Status][Discuss]

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)。

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

 
  这题我很久以前见到过,当时没敢动手,现在学了AC自动机后,果断拿来练手了。
  题意很简单,可以离线。
  这里在建完fail树后须将fail边反向,建成一棵fail树,如果能A到B有条边,那么代表trie树上到A的字符串是到B的字符串的后缀,这时来一遍DFS,记录每一个点的时间戳,即一个时间段,be[i]指i点自己的编号,也指其开始,en[i]指其结束。
  对于一个节点,它在fail树上的子树中,所有节点的fail都直接或间接的连向它,指它是那些的子串,被他们包含,而用时间戳可以迅速知道一个点是否是其子树。
  之后用离线做法处理答案,用bit维护。
 
 #include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=1e5+;
char S[maxn];
int Query[maxn][],ans[maxn],cntQ;
struct A_Cautomation{
int ch[maxn][],fail[maxn],end[maxn],fa[maxn],ID[maxn],cnt,root,cont;
int fir[maxn],nxt[maxn],to[maxn],cot;
int be[maxn],en[maxn],sjc;
void Init()
{
memset(ch,,sizeof(ch));
memset(fail,,sizeof(fail));
memset(end,,sizeof(end));
sjc=cot=cnt=cont=root=;
}
void Insert()
{
scanf("%s",S);
int len=strlen(S),node=root;
for(int i=;i<len;i++)
{
if(S[i]>='a'&&S[i]<='z'){
if(ch[node][S[i]-'`']){
node=ch[node][S[i]-'`'];
}
else{
fa[++cnt]=node;
node=ch[node][S[i]-'`']=cnt;
}
}
else{
if(S[i]=='B'){
node=fa[node];
}
else{
end[node]=++cont;
ID[cont]=node;
}
}
}
}
void Build()
{
queue<int>q;
for(int i=;i<=;i++){
if(ch[root][i]){
fail[ch[root][i]]=root;
q.push(ch[root][i]);
}
else
ch[root][i]=root;
}
while(!q.empty())
{
int node=q.front();q.pop();
for(int i=;i<=;i++){
if(ch[node][i]){
fail[ch[node][i]]=ch[fail[node]][i];
q.push(ch[node][i]);
}
else{
ch[node][i]=ch[fail[node]][i];
}
}
}
} void addedge(int a,int b)
{nxt[++cot]=fir[a];fir[a]=cot;to[cot]=b;} void DFS(int node)
{
be[node]=++sjc;
for(int i=fir[node];i;i=nxt[i])
DFS(to[i]);
en[node]=sjc;
} void BuildTree()
{
for(int i=;i<=cnt;i++)
addedge(fail[i],i); DFS(root);
} int bit[maxn];
void change(int k,int x)
{
while(k<=)
{
bit[k]+=x;
k+=k&(-k);
}
} int Quer(int k)
{
int ret=;
while(k)
{
ret+=bit[k];
k-=k&(-k);
}
return ret;
} void Solve()
{
memset(fir,,sizeof(fir));cot=;
memset(bit,,sizeof(bit));
for(int i=;i<=cntQ;i++)
Query[i][]=ID[Query[i][]],
Query[i][]=ID[Query[i][]],
addedge(Query[i][],Query[i][]); int len=strlen(S),node=root;
for(int i=;i<len;i++)
{
if(S[i]>='a'&&S[i]<='z'){
node=ch[node][S[i]-'`'];
change(be[node],);
}
else{
if(S[i]=='B'){
change(be[node],-);
node=fa[node];
}
else{
for(int i=fir[node];i;i=nxt[i]){
ans[i]=Quer(en[to[i]])-Quer(be[to[i]]-);
}
}
}
} for(int i=;i<=cntQ;i++)
printf("%d\n",ans[i]);
}
}AC; int main()
{
AC.Init();
AC.Insert();
AC.Build();
AC.BuildTree(); int Q;
scanf("%d",&Q);
while(Q--)
scanf("%d%d",&Query[cntQ][],&Query[++cntQ][]); AC.Solve();
return ;
}

AC自动机:BZOJ 2434 阿狸的打字机的更多相关文章

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

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

  2. BZOJ 2434 阿狸的打字机

    http://www.lydsy.com/JudgeOnline/problem.php?id=2434 思路:建立fail树,并找出dfs序,那剩下要做的就是每次找到一个串的位置,然后询问它的区间里 ...

  3. [NOI 2011][BZOJ 2434] 阿狸的打字机

    传送门 AC自动机 + 树状数组 建成AC自动机后,设end[i]为第i个串的末尾在Trie树上的节点. 可以发现,对于一个询问(x,y),ans等于Trie树上root到end[y]这条链上fail ...

  4. BZOJ 2434 阿狸的打字机 | AC自动机

    题目戳这里 AC自动机上有神奇的东西叫做fail指针--所有fail指针连起来恰好构成一棵以1为根的树! 而这道题问x在y中出现过多少次,就是问Trie树上根到y的结束节点的路径上有多少节点能通过跳f ...

  5. bzoj 2434 阿狸的打字机 - Aho-Corasick自动机 - 树状数组

    题目传送门 传送站I 传送站II 题目大意 阿狸有一个打字机,它有3种键: 向缓冲区追加小写字母 P:打印当前缓冲区(缓冲区不变) B:删除缓冲区中最后一个字符 然后多次询问第$x$个被打印出来的串在 ...

  6. BZOJ 2434 阿狸的打字机(fail树)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2434 题意:阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28 ...

  7. bzoj 2434: 阿狸的打字机 fail树+离线树状数组

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=2434 题解: 首先我们可以发现这个打字的过程本身就是在Trie上滚来滚去的过程 所以我们 ...

  8. bzoj 2434 阿狸的打字机 fail树的性质

    如果a串是另b串的后缀,那么在trie图上沿着b的fail指针走一定可以走到a串. 而a串在b串里出现多少次就是它是多少个前缀的后缀. 所以把fail边反向建树维护个dfs序就行了. 并不是很难... ...

  9. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

随机推荐

  1. Linux PATH变量的设置

    一般Linux系统,有两个配置文件可以设置PATH变量,一:.bashrc 二:.bash_profile; 还有一种方法可以临时设置PATH变量(三) 一:  1.编辑.bashrc,添加 expo ...

  2. centos6.5 64位 openvpn安装配置

    1 查看系统版本 2 cat /etc/redhat-release 3 CentOS release 6.5 (Final) 4 5 查看内核和cpu架构 6 uname -rm 7 2.6.32- ...

  3. eclipse alt + '/' not working.

    searching for google,I observed that the 'content assist' shortcut key was take placed with 'ctrl + ...

  4. C# 多个线程一直跑着While(true)

    在使用多线程的时候,开了多个线程一直在While(true),会造成CPU占用很高.这时候要在线程内加入一句Thread.Sleep(1),让他稍微睡一下.就不会消耗那么多CPU了. 代码: Thre ...

  5. Linux通配符

    * 任意字符 ?任意单个字符 [] 匹配指定 字符范围内的字符 [^] 指定范围之外的单个字符 常规字符集合 [a-z] a到z的所有小写字母 [A-Z] a到z的所有大写字母 [0-9] 0到9的所 ...

  6. KTV2

    自制KTV点歌系统经验 重唱与切歌 1.重唱 这个简单,会了播放,就会了这个; 我们用的数组下标来确定播放歌曲,自然如果下标没变的话,那播放的就还是这首了.所以只要确定了要执行的命令是重唱就行了. 那 ...

  7. Visual Studio vs2010 去掉中文注释红色下划线;去掉代码红色下划线;

    vs去掉下挂线也分两种: 1.去掉中文注释红色下划线,需要去掉VisualAssist下划线鸡肋功能: 1.选择Visual AssistX Options: 2.把如图所示的勾去掉,解决. 以后再次 ...

  8. php数组(array)输出三种形式

    $bbbb=array("11"=>"aaa","22"=>"bbb"); //只能输出值value不能输出 ...

  9. chdir 改变当前目录为起始目录

    <?php chdir(dirname(__FILE__));//把当前目录设置为当前目录?> 将 PHP 的当前目录改为 directory. 参数 directory 新的当前目录 返 ...

  10. 用jQuery实现瀑布流效果学习笔记

    jQuery一直没系统的学,只知道是js库,封装了好多js函数,方便了开发.以前做过一个原生的图片网站瀑布流效果,超级麻烦,这次用了jQuery方法,瞬间代码浓缩了,只有56行js代码.神奇的让我来把 ...