Problem

Description

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 \(28\) 个按键,分别印有 \(26\) 个小写英文字母和 BP 两个字母。 经阿狸研究发现,这个打字机是这样工作的:

  • 输入小写字母,打字机的一个凹槽中会加入这个字母(按 P 前凹槽中至少有一个字母)。
  • 按一下印有 B 的按键,打字机凹槽中最后一个字母会消失。
  • 按一下印有 P 的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失(保证凹槽中至少有一个字母)。

例如,阿狸输入 aPaPBbP ,纸上被打印的字符如下:

a
aa
ab

我们把纸上打印出来的字符串从 \(1\) 开始顺序编号,一直到 \(n\) 。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数 \((x,y)\) (其中 \(1 \le x,y \le n\) ),打字机会显示第 \(x\) 个打印的字符串在第 \(y\) 个打印的字符串中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

Input Format

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数 \(m\) ,表示询问个数。 接下来 \(m\) 行描述所有由小键盘输入的询问。其中第i行包含两个整数 \(x, y\) ,表示第i个询问为 \((x, y)\) 。

Output Format

输出 \(m\) 行,其中第 \(i\) 行包含一个整数,表示第 \(i\) 个询问的答案。

Sample

Input

aPaPBbP
3
1 2
1 3
2 3

Output

2
1
0

Range

所有测试数据的范围和特点如下表所示:

测试点编号 \(n\) 的规模 \(m\) 的规模 字符串长度 输入总长 (输入文件第一行的字符数)
1 \(1\le n \le 100\) \(1\le m \le 1000\) - \(\le 100\)
2 \(1\le n \le 100\) \(1\le m \le 1000\) - \(\le 100\)
3 \(1\le n \le 1000\) \(1\le m \le 10^4\) 单个长度 \(\le 1000\) ,总长度 \(\le 10^5\) \(\le 10^5\)
4 \(1\le n \le 1000\) \(1\le m \le 10^4\) 单个长度 \(\le 1000\) ,总长度 \(\le 10^5\) \(\le 10^5\)
5 \(1\le n \le 10^4\) \(1\le m \le 10^5\) 总长度 \(\le 10^5\) \(\le 10^5\)
6 \(1\le n \le 10^4\) \(1\le m \le 10^5\) 总长度 \(\le 10^5\) \(\le 10^5\)
7 \(1\le n \le 10^4\) \(1\le m \le 10^5\) 总长度 \(\le 10^5\) \(\le 10^5\)
8 \(1\le n \le 10^5\) \(1\le m \le 10^5\) - \(\le 10^5\)
9 \(1\le n \le 10^5\) \(1\le m \le 10^5\) - \(\le 10^5\)
10 \(1\le n \le 10^5\) \(1\le m \le 10^5\) - \(\le 10^5\)

Algorithm

\(Trie\) 图,树状数组

Mentality

欲得正解,先想暴力。

思考一下暴力怎么做:建 \(Trie\) -> 建 \(fail\) -> 对于每对询问 \(x,y\),由于 \(y\) 已经插入了 \(Trie\) 里,直接从 \(y\) 对应的终止结点往根走,并在每个点跳 \(fail\) 来计算答案。

当然,为了后面的分数,建 \(Trie\) 的时侯我们还是不能这么裸的,应该利用不同字符串间的高度重复性。由于打字机必需从末尾一个个删除字母,我们可以维护一个指针 \(pos\) 指向上个字符串的结尾,每当运行一次删除操作且此次操作会导致当前字符串与上一字符串的前缀重合长度 \(-1\) ,就令 \(pos\) 跳至父亲结点处。当执行打印命令时,则从 \(pos\) 处开始建 \(Trie\) 即可。

不难知道,这样我们的得分是 \(40\) 。

接下来,我们发现,\(fail\) 指针是构成了一棵树的,那么我们本来对于询问的暴力跳跃就可以得到转化:在 \(x\) 的 \(fail\) 边子树内,有多少个结点属于 \(y\) ?

那接下来就很明显了:

  • 先预处理出 \(fail\) 树上的 \(dfn\) 序,然后离线询问,将询问挂载在 \(y\) 的终止结点上。

  • 然后对 \(Trie\) 做 \(dfs\) ,每当进入一个结点就在对应的 \(fail\) 树上的 \(dfn\) 序位置 \(+1\) ,当退出一个结点就 \(-1\) 。这样一来,每当到达一个结点,就有且仅有根结点到当前结点路径上的点对应位置 \(+1\) ,满足了询问的统计条件。

  • 每到一个询问点 \(x\) ,查询 \([dfn[x],dfn[x]+size[x]-1]\) 区间内的 \(1\) 的个数即为答案。

Code

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
void read(int &x) {x = 0;char ch = getchar();while (!isdigit(ch)) ch = getchar();while (isdigit(ch)) {x = x * 10 + ch - '0';ch = getchar();}
}
const int Max_n = 1e5 + 1, Max_m = 1e5 + 1, Max_len = 1e5 + 1, M = 26;
int n, m, Ans[Max_n];
int top, pos, cnt_T, End[Max_n];
char S[Max_len], now_S[Max_len];
vector<int> que_d[Max_len], que_x[Max_len];
queue<int> q;
void Trie_add(int x);
struct Trie {int sur, ch[M], fa, deep, End;int nx(int x, int now) {if (!ch[x]) {ch[x] = ++cnt_T;Trie_add(now);}return ch[x];}
} k[Max_len], tp[Max_len];
void Trie_add(int x) { k[cnt_T].fa = x, k[cnt_T].deep = k[x].deep + 1; }
void Trie_build() {for (int i = 0; i < M; i++)if (k[0].ch[i]) q.push(k[0].ch[i]);while (!q.empty()) {int x = q.front();q.pop();for (int i = 0; i < M; i++)if (k[x].ch[i])k[k[x].ch[i]].sur = k[k[x].sur].ch[i], q.push(k[x].ch[i]);elsek[x].ch[i] = k[k[x].sur].ch[i];}
}
int cntr, head[Max_len], nx[Max_len], to[Max_len];
int cntd, d[Max_len], size[Max_len];
int cnts, s[Max_len << 1];
int c[Max_len];
bool vis[Max_len];
void get_s(int x) {s[++cnts] = x;for (int i = 0; i < M; i++)if (k[x].ch[i]) get_s(k[x].ch[i]);s[++cnts] = x;
}
void addr(int u, int v) {cntr++;to[cntr] = v, nx[cntr] = head[u];head[u] = cntr;
}
void Tree_build(int x) {size[x] = 1, d[x] = ++cntd;for (int i = head[x]; i; i = nx[i]) Tree_build(to[i]), size[x] += size[to[i]];
}
void c_add(int k, int x) {if (!k) return;for (int i = k; i <= cnt_T + 1; i += i & -i) c[i] += x;
}
int c_query(int k) {int ans = 0;for (int i = k; i; i -= i & -i) ans += c[i];return ans;
}
void Ans_Count(int x) {for (int i = 1; i <= cnts; i++) {int x = s[i];if (!vis[x]) { // 第一次访问代表进入结点vis[x] = 1, c_add(d[x], 1);for (int j = que_d[x].size() - 1; ~j; j--) {int now = que_x[x][j];Ans[que_d[x][j]] =c_query(d[now] + size[now] - 1) - c_query(d[now] - 1);}} else // 第二次访问代表退出结点c_add(d[x], -1);}
}
int main() {scanf("%s", S);for (int i = 0, lim = strlen(S); i < lim; i++) {if (S[i] == 'B')top--, pos = k[pos].fa; // pos 的处理else if (S[i] == 'P') {for (int p = k[pos].deep + 1; p <= top; p++) // pos 的使用pos = k[pos].nx(now_S[p] - 'a', pos);k[pos].End = ++n;End[n] = pos;} elsenow_S[++top] = S[i];}get_s(0); // 先处理出 Trie 树上点的访问顺序Trie_build(); // fail 指针构建read(m);int x, y;for (int i = 1; i <= m; i++) {read(x), read(y);que_d[End[y]].push_back(i);que_x[End[y]].push_back(End[x]); // 挂载询问}for (int i = 1; i <= cnt_T; i++) addr(k[i].sur, i); // 连接 fail 边Tree_build(0); // 构建 fail 树Ans_Count(0); // 统计答案for (int i = 1; i <= m; i++) printf("%d\n", Ans[i]);
}

转载于:https://www.cnblogs.com/luoshuitianyi/p/11054782.html

【NOI 2011】阿狸的打字机相关推荐

  1. [NOI 2011]阿狸的打字机

    Description 题库链接 给你 \(n\) 个单词, \(m\) 组询问,每组询问形同 \((x,y)\) ,询问 \(x\) 串在 \(y\) 串中出现多少次. \(1\leq n,m\le ...

  2. 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

    [BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...

  3. [bzoj 2434][Noi2011]阿狸的打字机

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2434 [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory ...

  4. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 2545  Solved: 1419 [Submit][S ...

  5. 2434: [Noi2011]阿狸的打字机

    2434: [Noi2011]阿狸的打字机 https://lydsy.com/JudgeOnline/problem.php?id=2434 分析: AC自动机. 查询x在y中出现了几次,就是查询y ...

  6. P2414 NOI2011阿狸的打字机 [AC自动机,dfs序]

    阿狸的打字机 题解 题目中给出的字符串就是构建TrieTrieTrie树的顺序.我们将字符串依次读入,每读入一个小写字符就相当于在TrieTrieTrie树当前节点下插入一个小写字符,读入BBB时,就 ...

  7. 【bzoj 2434】【codevs 1946】[Noi2011]阿狸的打字机(AC自动机)

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 2477  Solved: 1382 [Submit][S ...

  8. bzoj 2434 [Noi2011]阿狸的打字机(AC自动机+fail树+dfs序+树状数组)

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 3521  Solved: 1913 [Submit][S ...

  9. [NOI2011] 阿狸的打字机

    [NOI2011] 阿狸的打字机 题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有 28 28 28 个按键,分别印有 26 26 26 个小写英文字母和 B.P 两 ...

最新文章

  1. 使用MakeCAB.exe命令创建CAB文件
  2. js 拖动层示例[转]
  3. spring 定时器任务深入理解
  4. 深入浅出:Linux设备驱动之字符设备驱动
  5. HDU 4850 Wow! Such String! 【欧拉回路】【一顿乱构造】
  6. C# 小工具开发--DeBugHttp
  7. Beaglebone bootloader杂谈
  8. java mqtt客户端_基于 t-io 实现一个 mqtt5 协议之 mica-mqtt
  9. 【渝粤题库】广东开放大学 综合英语1 形成性考核 (2)
  10. 结对开发:电梯调度(2)
  11. JDK源码解析之 Java.lang.Compiler
  12. kotlin 查找id_Kotlin程序查找等边三角形的区域
  13. L1-065 嫑废话上代码 (5 分)-PAT 团体程序设计天梯赛 GPLT
  14. atitit.新增编辑功能 跟orm的实现 attilax p31
  15. 淘淘商城项目mysql,idea搭建淘淘商城项目
  16. Tomcat内存大小配置及查看内存情况
  17. ps基础学习:图层叠加
  18. python多条件求和_使用sumifs进行多条件求和
  19. 英语四级口语测试软件,2021年大学英语四级口语测试题
  20. 计算机会不会取代人类英语作文,高中英语作文:机器代替人?

热门文章

  1. 【图像分类】简述无监督图像分类发展现状
  2. 全球及中国手持式吸尘器行业供应需求及未来投资潜力预测报告2022-2027年
  3. leetcode 214. 最短回文串 解题报告
  4. Java面试总结(2018 - 12 - 10)
  5. CASE WHEN 高阶用法?
  6. 2016级算法第一次练习赛-E.AlvinZH的儿时回忆——蛙声一片
  7. javascript创建对象 1
  8. POJ1328-Radar Installation
  9. as3corelib系列教程之一:ArrayUtil类的用法
  10. 海外客户的营销新思路(内容营销)