[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 ...
随机推荐
- dos2unix命令详解
基础命令学习目录首页 原文链接:https://blog.csdn.net/leedaning/article/details/53024290 使用git 的时候碰到git将unix换行符转换为wi ...
- Daily Srum 10.30
Android那一组打算用SQL Server这个关系型数据库,而王鹿鸣他们一组却是依赖于Hbase,这是一件很麻烦的事,所以我打算在这两方面都建立一个数据库.虽然挺麻烦,但是还是为了扩展性所做的必要 ...
- caffe添加自己的数据输入层
整体思路: 阅读caffe数据输入层各个类之间的继承关系,确定当前类需要继承的父类以及所需参数的设置. 编写zzq_data.cpp 在layer_factory.cpp中完成注册: 在caffe.p ...
- Android笔记-5-EditText密码和Checkbox二选一
EditText密码:明文和密文 密文: public class MainActivity extends Activity { private EditText password = null; ...
- NABCD(校园包车)
广州商学院包车 N(need) 各个高校包车需求量大,然而校园内包车信息太散乱,售票地点不确定,有些老师.学生特别是新生,甚至不知有校园包车这一回事, 随着信息网络的发展,为了给师生带来校园更多的方便 ...
- 索引超出了数组界限。 在 System.Collections.Generic.Dictionary`2.Resize
博问:Dictionary 超出了数组界限 异常: Exception type: IndexOutOfRangeException Exception message: 索引超出了数组界限. 在 S ...
- java属性编辑器,即PropertyEditor
出处:http://www.iteye.com/topic/1123628
- 《软件工程和Python》PYTHON效能分析和Django
资料汇总网站:http://www.yzhiliao.com/my/course/55 一..作业下面两个题目任选一题: (1)运用jieba库分词(或者你喜欢的其他库),并把代码发到git上去(不发 ...
- Matlab图像匹配问题
已知一个任意形状,查找在大图像中最接近的形状位置. 输入:一个小图形状和一张大图 输出:最接近的形状在大图中的位置 假设: (1)已知形状与目标形状有一定的形变. (2)形状与大图像均为二值图像,图中 ...
- week3c:个人博客作业
程序测试: 一个基本的测试. 在Visual Studio 2013 中使用C++单元测试 操作如下: 这是我学到的过程. 有复杂程序的测试.以后有时间再弄.