【bzoj2434-阿狸的打字机】AC自动机+fail树+优化
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23083
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Sample Input
aPaPBbP
3
1 2
1 3
2 3
Sample Output
2
1
0
题解:这道题做了好久。。哭泣。。。。。。。
主要是超时的问题。
fail树:按照AC自动机上fail的反向边建立的树。
如样例:

可以知道:failtree上,任意一点所代表的子串(即以trie上根到该点的串)是它在fail树上的子树任意一点的子串。
就是说,x是y的祖先,那么以y为结尾的串一直fail可以fail到x,就是x是y的子串。
那么现在可以得出一个简单粗暴的方法:
对于每个询问(x,y),求x在y上出现的次数,我们就可以在fail树上找到x串末尾的点A,然后询问y串在fail树上的每一个点是否是x的子树中的点,是就ans++。
但是明显,这个方法慢出天际了。。。。
优化:
(三颗星)离线。在fail树上用dfn给节点编号,则每一个点的子树在节点号上是连续的一段。在trie上走一遍,每到一个点x就在树状数组的dfn[x]位置+1,每离开一个点就在树状数组的dfn[x]位置-1。然后每当走到一个串的末尾,就询问每个对应要询问的x串,给fail树上它的子树求和,就是树状数组上getsum A~next[A]-1,这样就求出了y上多少个点为末尾可以fail到x,就是该询问(x,y)的答案。
后面就是我的错误点了:
(1)循环字符串的时候不要打for(int i=0;i<strlen(s);i++) ... 拿个变量存一下strlen(s)。。。超慢的。。
(2)做trietree的时候不用每次从头开始。。接着上一次的末尾就可以了。。快超多。。
(3)最后询问的时候不要把所有询问扫一遍。。用个first存y的边目录啊。。
血与泪啊。。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
using namespace std; const int N=,S=;
struct node{
int k,fa,fail,son[];
}a[N];
struct edge{
int x,y,next,fa,ans;
}b[N],t[N];
char s[N];
int pre,m,tl,sl,num,cnt_s,len,cnt_dfn;
int last[N],c[N],sk[N],dfn[N],next[N],first[N],fir[N];
queue<int> q; void clear(int x)
{
a[x].fa=a[x].k=a[x].fail=;
memset(a[x].son,,sizeof(a[x].son));
} void buildAC()
{
while(!q.empty()) q.pop();
for(int i=;i<=S;i++)
if(a[].son[i]) q.push(a[].son[i]);
while(!q.empty())
{
int x=q.front();q.pop();
int fail=a[x].fail;
for(int i=;i<=S;i++)
{
if(a[x].son[i])
{
a[a[x].son[i]].fail=a[fail].son[i];
q.push(a[x].son[i]);
}
else a[x].son[i]=a[fail].son[i];
}
}
} void ins(int x,int y,bool bk)
{
if(!bk)
{
b[++len].x=x;b[len].y=y;
b[len].next=first[x];first[x]=len;
}
else
{
t[++tl].x=x;t[tl].y=y;t[tl].ans=;
t[tl].next=fir[x];fir[x]=tl;
}
} void dfs(int x)
{
dfn[x]=++cnt_dfn;
for(int i=first[x];i;i=b[i].next)
{
int y=b[i].y;
dfs(y);
}
next[x]=cnt_dfn;
} void build_fail_tree()
{
for(int i=;i<=num;i++) ins(a[i].fail,i,);
dfs();
} int lowbit(int x){return x&(-x);}
void add(int x,int d){for(int i=x;i<=N-;i+=lowbit(i)) c[i]+=d;}
int getsum(int x)
{
int ans=;
for(int i=x;i>=;i-=lowbit(i)) ans+=c[i];
return ans;
} int main()
{
freopen("a.in","r",stdin);
freopen("me.out","w",stdout);
num=;cnt_s=;cnt_dfn=;len=;pre=;tl=;clear();
memset(fir,,sizeof(fir));
memset(first,,sizeof(first));
memset(c,,sizeof(c));
scanf("%s",s);
//build trie tree
int strl=strlen(s),now=;
for(int i=;i<strl;i++)
{
if(s[i]=='P')
{
a[now].k=;
last[++cnt_s]=now;
}
else if(s[i]=='B') now=a[now].fa;
else
{
int ind=s[i]-'a'+;
if(!a[now].son[ind])
{
num++;clear(num);
a[now].son[ind]=num;
a[num].fa=now;
}
now=a[now].son[ind];
}
}
//build ac
buildAC();
//build fail tree
build_fail_tree();
scanf("%d",&m);
for(int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(y,x,);
}
//find
now=,cnt_s=;
for(int i=;i<strl;i++)
{
if(s[i]=='B')
{
add(dfn[now],-);
now=a[now].fa;
}
else if(s[i]=='P')
{
cnt_s++;
for(int j=fir[cnt_s];j;j=t[j].next)
{
int st=dfn[last[t[j].y]],ed=next[last[t[j].y]];
t[j].ans=getsum(ed)-getsum(st-);
}
}
else
{
int ind=s[i]-'a'+;
now=a[now].son[ind];
add(dfn[now],);
}
}
for(int i=;i<=m;i++)
printf("%d\n",t[i].ans);
return ;
}
【bzoj2434-阿狸的打字机】AC自动机+fail树+优化的更多相关文章
- 【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2022 Solved: 1158[Submit][Sta ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 2434: [Noi2011]阿狸的打字机 AC自动机+fail树+线段树
Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的 ...
- [NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]
题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 ...
- BZOJ2434:[NOI2011]阿狸的打字机(AC自动机,线段树)
Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的 ...
- BZOJ 2434 Luogu P2414 [NOI2011]阿狸的打字机 (AC自动机、树状数组)
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2434 题解: 我写的是离线做法,不知道有没有在线做法. 转化一波题意,\(x\)在AC ...
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MB Submit: 3610 Solved: 1960 [Submit][S ...
- 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组
[BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...
随机推荐
- [转]STUN和TURN技术浅析
[转]STUN和TURN技术浅析 http://www.h3c.com.cn/MiniSite/Technology_Circle/Net_Reptile/The_Five/Home/Catalog/ ...
- java程序用做windows服务
具体步骤在这里 http://www.doc88.com/p-360144091164.html 遇到错误: JVM did not exit on request, terminated 通过下面的 ...
- HashSet<T>类
HashSet<T>类主要是设计用来做高性能集运算的,例如对两个集合求交集.并集.差集等.集合中包含一组不重复出现且无特性顺序的元素. HashSet<T>的一些特性如下: 1 ...
- 51.ISE中的DCM全局时钟转为普通IO
在用DCM这个IP核时,它的输入时钟为全局时钟引脚输入,输出有两种情况,第一,可以直接接在全局时钟引脚:第二,可以通过ODDR2原语接在普通IO引脚:说下第二种是怎么用的: DCM DCM_INST ...
- python并行迭代
并行迭代:同时并行遍历两个列表 for line1,line2 in zip(line1_list, line2_list): ... 无聊,贴一段刚才的代码: import sys import s ...
- [shell基础]——split命令
测试文本 # cat name1.txt name1 alvin1 name2 alvin2 name3 alvin3 name4 alvin4 此时目录下就只有这个文件 # ls name1.txt ...
- UIScrollView缩放图片操作
要想ScrollView缩放,必须告诉缩放那个控件,它自身的大小是不会缩放的: 并且ScrollView只能缩放自己内部的子控件: 1:这时就要用到代理,代理告诉ScrollView缩放哪个控件.(设 ...
- 制作C/C++动态链接库(dll)若干注意事项
一.C\C++ 运行时库编译选项简单说明 问题:我的dll别人没法用 运行时库是个很复杂的东西,作为开发过程中dll制作需要了解的一部分,这里主要简单介绍一下如何选择编译选项. 在我们的开发过程中时常 ...
- java中的substring用法
String str="我是中国人"; str = str.substring(0, 2) +"_"+str.substring(3, 4); 结果:str=& ...
- shell 进程查询相关的命令
同一个进程部署多个,如何知道删除哪个: ll /proc/ 会显示位置 查看pid下哪些socket链接 ll /proc//fd