[Luogu P2414] [BZOJ 2434] [NOI2011]阿狸的打字机

洛谷传送门
BZOJ传送门

题目背景

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。

题目描述

打字机上只有 28 28 28个按键,分别印有 26 26 26个小写英文字母和 B B B P P P两个字母。经阿狸研究发现,这个打字机是这样工作的:

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

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

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

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

输入输出格式

输入格式:

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

第二行包含一个整数 m m m,表示询问个数。

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

输出格式:

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

输入输出样例

输入样例#1:
aPaPBbP
3
1 2
1 3
2 3
输出样例#1:
2
1
0

说明

数据范围:

对于 100 % 100\% 100%的数据, n ≤ 100000 , m ≤ 100000 n\le 100000,m\le 100000 n100000,m100000,第一行总长度 ≤ 100000 \le 100000 100000

img

解题分析

A C AC AC自动机好题。 建自动机什么的都好说, 只是额外维护一个父亲节点表示从其转移过来的即可完成回退操作。

问题在于如果我们每次暴力匹配复杂度会变成 O ( N 2 ) O(N^2) O(N2)的。 如何优化?

考虑我们是如何暴力的: 对 x x x串建立 A C AC AC自动机, 将 y y y串依次插入, 暴力跳 f a i l fail fail, 看是否能到达 x x x串的终止位置。 我们发现, 只要插入的字符在 f a i l fail fail树上在 x x x串终止位置的子树中, 就会产生贡献。

这样就好做了:我们离线询问, D F S DFS DFS整颗 t r i e trie trie树, 每到达一个节点就将其在 f a i l fail fail树上的 d f s dfs dfs序处的贡献 + 1 +1 +1。 如果到达了某个串的终止节点, 就回答其作为 y y y串的所有询问(直接查询 x x x f a i l fail fail树中的子树贡献之和)。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <queue>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define lbt(i) ((i) & (-(i)))
#define MX 100500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int cnt, m, tot, dfn, root;
int son[MX][26], fat[MX], fail[MX], ed[MX], lb[MX], rb[MX], head[MX], tree[MX], p[MX], ans[MX];
char buf[MX];
struct INFO {int tar, tim;};
std::vector <int> to[MX];
std::vector <INFO> que[MX];
std::queue <int> q;
IN void add(R int pos, R int del) {for (; pos <= cnt + 1; pos += lbt(pos)) tree[pos] += del;}
IN int query(R int pos) {int ret = 0; for (; pos; pos -= lbt(pos)) ret += tree[pos]; return ret;}
IN void insert(char *str)
{
	R int now = root, len = std::strlen(str), id;
	for (R int i = 0; i < len; ++i)
	{
		if (str[i] == 'P') ed[now] = ++tot, p[tot] = now;
		else if (str[i] == 'B') now = fat[now];
		else
		{
			id = str[i] - 'a';
			if (!son[now][id]) son[now][id] = ++cnt, fat[son[now][id]] = now;
			now = son[now][id];
		}
	}
}
void build()
{
	R int now, cur; fail[0] = -1;
	for (R int i = 0; i < 26; ++i) if (son[root][i]) q.push(son[root][i]), to[root].push_back(son[root][i]);
	W (!q.empty())
	{
		now = q.front(); q.pop();
		for (R int i = 0; i < 26; ++i)
		{
			if (son[now][i])
			{
				cur = fail[now];
				W ((~fail[cur]) && (!son[cur][i])) cur = fail[cur];
				fail[son[now][i]] = son[cur][i];
				q.push(son[now][i]); to[fail[son[now][i]]].push_back(son[now][i]);
			}
		}
	}
}
void DFS1(R int now)
{
	lb[now] = ++dfn;
	for (R int i = to[now].size() - 1; ~i; --i) DFS1(to[now][i]);
	rb[now] = dfn;
}
void DFS2(R int now)
{
	add(lb[now], 1);
	if (ed[now])
	{
		INFO cur;
		for (R int i = que[now].size() - 1; ~i; --i)
		{
			cur = que[now][i];
			ans[cur.tim] = query(rb[p[cur.tar]]) - query(lb[p[cur.tar]] - 1);
		}
	}
	for (R int i = 0; i < 26; ++i)
	if (son[now][i]) DFS2(son[now][i]);
	add(lb[now], -1);
}
int main(void)
{
	int x, y;
	scanf("%s", buf); insert(buf);
	in(m);
	for (R int i = 1; i <= m; ++i)
	{
		in(x), in(y);
		que[p[y]].push_back({x, i});
	}
	build(); DFS1(0); DFS2(0);
	for (R int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值