传送门

AC自动机 + 树状数组

建成AC自动机后,设end[i]为第i个串的末尾在Trie树上的节点。

可以发现,对于一个询问(x,y),ans等于Trie树上root到end[y]这条链上fail指针指向end[x]的节点数,我们把这些点记为特殊点

因为Trie树上每个节点fail指针仅指向一个值,

因此可以将fail指针反转构建一棵树,以下称为fail树。

于此答案可以等价于在fail树上以end[x]为根的子树中存在的特殊点个数。

然而这样暴力做还是过不了。于是需要一些优化。

可以知道将一个树的节点按dfs序排列后,

树的任意一颗子树的节点在序列中都是连续的一段区间。

我们把特殊的点记为1,非特殊点记为0,

于是求某子树上的特殊点个数可以转化成求某区间的和,

于是就可以用树状数组来优化了。(线段树应该也可以吧,不过似乎会比较麻烦)。

代码:

 #include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring> using namespace std; #define N ((1<<17)-1)
#define M ((1<<17)-1)
#define lowbit(x) ((x)&(-(x))) queue<int>q;
char s[N];
int end[N],n; struct ACA
{
int cnt;
int fa[N],son[N][],fail[N];
void get_trie()
{
cnt=;
int i,now=,v,p=;
for (i=;i<n;i++)
switch(s[i])
{
case 'P':{end[++p]=now;break;}
case 'B':{now=fa[now];break;}
default:
{
v=s[i]-'a';
if (!son[now][v])
{
son[now][v]=++cnt;
fa[cnt]=now;
}
now=son[now][v];
}
}
for (i=;i<;i++) son[][i]=;
}
void get_fail()
{
int i,j,x;
q.push();
while (!q.empty())
{
x=q.front(),q.pop();
for (i=;i<;i++)
if (son[x][i])
{
for (j=fail[x];j&&!son[j][i];j=fail[j]);
fail[son[x][i]]=son[j][i];
q.push(son[x][i]);
}
}
}
}aca; struct BIT
{
int arr[N],l[N],r[N];
int sum(int i)
{
int re=;
while (i)
{re+=arr[i];i-=lowbit(i);}
return re;
}
void add(int i,int k)
{
while (i<=aca.cnt)
{arr[i]+=k;i+=lowbit(i);}
}
}bit; struct Tree
{
vector<vector<int> >son;
void addedge(int u,int v)
{
son[u].push_back(v);
}
void build()
{
son.resize(aca.cnt+);
for (int i=;i<=aca.cnt;i++)
addedge(aca.fail[i],i);
}
void dfs_order(int x,int &k)
{
bit.l[x]=bit.r[x]=k++;
for (int i=;i<son[x].size();i++)
{
dfs_order(son[x][i],k);
bit.r[x]=max(bit.r[x],bit.r[son[x][i]]);
}
}
}tree; int x[M],ans[M],pre[N],now[N]; inline int val(int x)
{
return bit.sum(bit.r[x])-bit.sum(bit.l[x]-);
} void answer()
{
int m,i,j,y,v,p;
scanf("%d",&m);
memset(now,,sizeof(now));
for (i=;i<=m;i++)
{
scanf("%d%d",&x[i],&y);
x[i]=end[x[i]];
y=end[y];
pre[i]=now[y];
now[y]=i;
}
for (p=,i=;i<n;i++)
{
switch(s[i])
{
case 'P':
{
for (j=now[p];j;j=pre[j])
ans[j]=val(x[j]);
break;
}
case 'B':
{
bit.add(bit.l[p],-);
p=aca.fa[p];
break;
}
default:
{
v=s[i]-'a';
p=aca.son[p][v];
bit.add(bit.l[p],);
}
}
}
for (i=;i<=m;i++)
printf("%d\n",ans[i]);
} int main()
{
scanf("%s",s);
n=strlen(s);
aca.get_trie();
aca.get_fail();
tree.build();
int k=;
tree.dfs_order(,k);
answer();
}

[NOI 2011][BZOJ 2434] 阿狸的打字机的更多相关文章

  1. AC自动机:BZOJ 2434 阿狸的打字机

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

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

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

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

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

  4. BZOJ 2434 阿狸的打字机

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

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

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

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

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

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

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

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

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

  9. 【BZOJ】【2434】【NOI2011】阿狸的打字机

    AC自动机+DFS序+BIT 好题啊……orz PoPoQQQ 大爷 一道相似的题目:[BZOJ][3172][TJOI2013]单词 那道题也是在fail树上数有多少个点,只不过这题是在x的fail ...

随机推荐

  1. 浅谈游戏中BUFF的设计要点

    其实这类帖子并没有多少的设计理论,对于策划的提升和帮助也并不大,原因其实在于其适用性太窄,当我要设计XX象棋的时候,它就滚一边去了. 废话不多说切入正题: 游戏中的BUFF/DEBUFF我们见过很多, ...

  2. 设计模式之Builder

    设计模式总共有23种模式这仅仅是为了一个目的:解耦+解耦+解耦...(高内聚低耦合满足开闭原则) 介绍: Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象. 将一个复杂 ...

  3. zuul进行rate limit

    maven <dependency> <groupId>com.marcosbarbero.cloud</groupId> <artifactId>sp ...

  4. Java垃圾收集算法

    算法名称 过程 优缺点 1. 标记-清除算法 (Mark-Sweep) 分为两个阶段: 1.首先标记出所有需要回收的对象: 2.在标记完成后统一回收所有被标记的对象. 缺点: 1.效率问题:标记和清除 ...

  5. redis基础之开机自启动和监听(二)

    redis安装好后,每次手动启动很不方便,配置开机自启动. 方法一:设置启动命令到/etc/rc.d/rc.local rc.local文件是系统全局脚本文件,会在其他开机进程脚本文件执行完毕后执行该 ...

  6. CentOS7手动编译安装内核4.11.7

    1. 进入/usr/src/目录 cd /usr/src 2. 下载内核源码,网址:https://www.kernel.org wget https://cdn.kernel.org/pub/lin ...

  7. (十七)vmware无法将网络更改为桥接状态

    故障现象,导致虚拟机无法正常上网 设备管理器中的驱动设备正常加载,但是注意这两个虚拟网卡是有问题的 将这两个虚拟网卡删除 只剩物理网卡了,重新启动电脑 将虚拟机里的网络设置删除 清空网卡后点击恢复默认 ...

  8. Linux 删除带有特殊字符的文件

    Linux 删除带有特殊字符的文件 http://www.cnblogs.com/tester-hehehe/p/5715128.html

  9. redis可编译

    redis-3.0.7 可编译 redis-3.0.7.tar.gz twemproxy-master.zip keepalived-1.2.19.tar.gz openssl-1.0.1s.tar. ...

  10. 2015多校第6场 HDU 5361 并查集,最短路

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5361 题意:有n个点1-n, 每个点到相邻点的距离是1,然后每个点可以通过花费c[i]的钱从i点走到距 ...