Problem
阿狸的打字机
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有个按键,分别印有个小写英文字母和、两个字母。
经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- 按一下印有的按键,打字机凹槽中最后一个字母会消失。
- 按一下印有的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入,纸上被打印的字符如下:
我们把纸上打印出来的字符串从开始顺序编号,一直到。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(其中),打字机会显示第个打印的字符串在第个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数,表示询问个数。
接下来行描述所有由小键盘输入的询问。其中第行包含两个整数, ,表示第个询问为。
Output
Sample Input
1 | aPaPBbP |
Sample Output
1 | 2 |
Hint
标签:AC自动机
Fail树
树状数组
Solution
这道题的提示还是很明显的。
读完题目,很容易发现此题打字的部分就是在建一棵。
输入小写字母即在中添加一个子结点并向儿子结点走,输入‘’即退回到父结点,输入’‘即在当前结点打标记。
因而我们可以构建如下:
1 | void init() { |
接下来我们对付这题的询问。
首先,它要求一个字符串在另一个字符串中出现多少次,这显然是自动机的操作,所以我们建立数组如下:
1 | void CalcFail() { |
现在我们考虑数组的实质。如果结点的指向结点,则结点代表的字符串一定是结点代表字符串的后缀,即经过的所有路径组成的字符串都包含结点代表的字符串。对于一个字符串,它的所有字串为它所有前缀的所有后缀,所以对于询问,我们需要找出从根节点到的路径中有多少结点可以通过指针转移到。
这时我们就需要考虑树了。对于任意结点,我们把所有通过指针能直接转移到的结点作为的子结点,而通过指针转移到的结点作为的父结点。这样我们就能构建一棵树。这样一来,对于询问,问题等价于从根到的结点中有多少节点是在的子树中。我们就可以用序操作。然后用树状数组维护。
为了使询问变得更好操作,我们考虑把询问按值排序,这样我们就只需一直往下走,然后标记经过的结点,然后统计子树即可。
Code
1 |
|