BZOJ 2434 阿狸的打字机 (AC自动机 fail树 树状数组)
来源:互联网 发布:天猫跟淘宝哪个质量好 编辑:程序博客网 时间:2024/06/08 03:46
2434: [Noi2011]阿狸的打字机
Time Limit: 10 Sec Memory Limit: 256 MB
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
题目大意:初始字串为空,首先给定一系列操作序列,有三种操作:
1.在结尾加一个字符
2.在结尾删除一个字符
3.打印当前字串
然后多次询问第x个打印的字串在第y个打印的字串中出现了几次
思路:
copy一下PoPoQQQ大佬的题解
首先构建AC自动机
令now为当前指针 初始为root 考虑三种操作
在结尾添加一个字符->新建一个子节点(若存在在不用新建),进入该子节点。在结尾删除一个字符->返回到父亲节点。打印当前字串->在当前节点标记是第几个字符串。
那么查询x在y中出现了几次 就是查询y有多少个节点沿着fail指针能找到x (AC自动机基本操作)
那么我们反向思考,查询y有多少个节点沿着fail指针能找到x 就是查询x沿着反向的fail指针能找到多少个y的节点。
fail指针没有环,每个节点只有一个出度,那么反向之后显然是一棵树,x沿着反向的fail指针所能到达的节点就是x所在的子树。
于是我们可以沿着反向的fail指针搞出DFS序,x所在的子树就是DFS中对应的区间 我们要查询的是x对应的区间中有多少个y。
对于每个y,我们把关于y的询问都存在邻接表里 然后把y所有的节点在DFS序中的位置插入树状数组,然后对于关于y的每个询问在树状数组上查询一遍即可。
求出DFS序之后 我们回来考虑这些操作序列
在结尾添加一个字符->添加的字符所在节点加入树状数组
在结尾删除一个字符->删除的字符所在节点从树状数组删除
打印当前字串-> 处理询问
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 100010; int len, tot, idc, cnt;int nextq[N], headq[N], toq[N];//邻接表存query int head[N], bit[N], pos[N], ans[N];int ch[N][26], fail[N], q[N], fa[N], in[N], out[N]; char s[N];struct Edge{ int next, to; }ed[N];void modify(int x, int val){ for( ; x<=cnt; x+=x&-x) bit[x] += val;}int query(int x){ int res = 0; for( ; x; x-=x&-x) res += bit[x]; return res; }//树状数组维护dfs序 void adde(int u, int v){ ed[++idc].to = v; ed[idc].next = head[u]; head[u] = idc;}void build(){ int now=0, idx=0; for(int i=0; i<len; i++) if(s[i]=='P') pos[++idx] = now;//记录第几个打出来的串,串尾在trie的pos else if(s[i] == 'B') now = fa[now];//删除当前节点 else{ if( !ch[now][s[i]-'a'] ) fa[ch[now][s[i]-'a'] = ++tot] = now;//新建节点 now = ch[now][s[i]-'a']; } }//trievoid Fail(){//bfs求fail int h = 0, t = 1; q[t] = 0; fail[0] = 0; while(h < t){ int u = q[++h]; for(int i=0; i<26; i++) if( ch[u][i] ){ q[++t] = ch[u][i]; fail[ch[u][i]] = u==0 ? 0:ch[fail[u]][i];//考虑根的特殊性 } else ch[u][i] = /*u==0 ? 0:*/ch[fail[u]][i];// }}void dfs(int u){//dfs序 in[u] = ++cnt; for(int k=head[u]; k; k=ed[k].next) dfs( ed[k].to ); out[u] = cnt; } void solve(){ int now=0, idx=0; modify(in[0], 1); for(int i=0; i<len; i++){ if(s[i] == 'P'){ idx++; for(int k=headq[idx]; k; k=nextq[k]){ int t = pos[toq[k]];//针对第idx个单词的询问 ans[k] = query(out[t]) - query(in[t]-1);//统计区间内的贡献 } } else if(s[i] == 'B') modify(in[now], -1), now = fa[now]; else now = ch[now][s[i]-'a'], modify(in[now], 1);//新加入一个字符就维护它的贡献 } } int main(){ int m; scanf("%s%d", s, &m); len = strlen(s); for(int i=1 ;i<=m; i++){ int x, y; scanf("%d%d", &x, &y); nextq[i] = headq[y], toq[i]=x, headq[y]=i;//query } build(); Fail(); for(int i=1; i<=tot; i++) adde(fail[i], i);//建反向fail树 dfs(0), solve(); for(int i=1; i<=m; i++) printf("%d\n", ans[i]); return 0; }
- BZOJ 2434 阿狸的打字机 (AC自动机 fail树 树状数组)
- bzoj 2434 [Noi2011]阿狸的打字机(AC自动机+fail树+dfs序+树状数组)
- [AC自动机 fail树 树状数组] BZOJ 2434 [NOI2011] 阿狸的打字机
- BZOJ 2434: [Noi2011]阿狸的打字机【AC自动机,fail树.dfs序,树状数组
- [BZOJ]2434 阿狸的打字机 AC自动机+Fail树+树状数组
- 阿狸的打字机 AC自动机 FAIL树 树状数组
- [省选前题目整理][BZOJ 2434][NOI 2011]阿狸的打字机(AC自动机+fail树+DFS序+树状数组)
- BZOJ 2434 阿狸的打字机 补全AC自动机 ( Trie图 ) fail树 树状数组 DFS序列
- bzoj 2434 阿狸的打字机 AC自动机+fail树
- BZOJ 2434 阿狸的打字机 (AC自动机 + 树状数组)
- bzoj2434 [Noi2011]阿狸的打字机 ( AC自动机 & fail树 + 树状数组 + dfs序 )
- [BZOJ2434]NOI2011阿狸的打字机|AC自动机|fail树|树状数组
- 【bzoj2434】[Noi2011]阿狸的打字机 AC自动机+fail树+dfs序+树状数组
- 【AC自动机-fail树+离线+DFS序+树状数组】BZOJ2434(Noi2011)[阿狸的打字机]题解
- BZOJ2434【NOI2011】阿狸的打字机 <AC自动机+Fail树+树状数组>
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
- bzoj 2434 (NOI2011)阿狸的打字机 【AC自动机】【树状数组】【DFS序】
- BZOJ 2434([Noi2011]阿狸的打字机-AC自动机-Fail树)
- urelsession 文件下载
- Qt Creator:DirMainWindow(对文件的操作)
- C++内存分配秘籍—new,malloc,GlobalAlloc,野指针详解
- linux中软件的安装
- 为什么使用css
- BZOJ 2434 阿狸的打字机 (AC自动机 fail树 树状数组)
- ElasticSearch: Index 和 Type 的区别
- poj 2774 Long Long Message(后缀数组)
- ListControl控件ICON模式
- Uva 340 猜数字的游戏
- oracle 数据字典
- 利用tensorflow 一步一步实现一个简单神经网络,线性回归
- 2017多校第二场 HDU 6050 Funny Function 推公式
- 1084. Broken Keyboard (20)