BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ)

-------------------------------------------------------------------------------------------

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<queue>

#include<cctype>

using namespace std;

#define chk(c) ((c <= ‘z‘ && c >= ‘a‘) || (c ==‘P‘) || (c == ‘B‘))

#define C(c) (c - ‘a‘)

const int maxn = 100009;

const int c = 26;

inline int read() {

char c = getchar();

for(; !isdigit(c); c = getchar());

int ret = 0;

for(; isdigit(c); c = getchar())

ret = ret * 10 + c - ‘0‘;

return ret;

}

struct Node {

Node *ch[c], *fail, *par;

int v, d;

Node() : v(0) {

memset(ch, 0, sizeof ch);

fail = par = 0;

}

} pool[maxn], *V[maxn], *pt = pool, *Rt;

struct Q {

int d, x, y;

inline void Read(int _d) {

d = _d;

x = read();

y = read();

}

bool operator < (const Q &o) const {

return y < o.y;

}

} q[maxn];

int n, dfsn, qn;

int L[maxn], R[maxn], ql[maxn], qr[maxn], ans[maxn];

void Init() {

int cnt = dfsn = n = 0;

pt->d = n++;

Rt = pt++;

Node* t = Rt;

char c = getchar();

for(; !chk(c); c = getchar());

for(; chk(c); c = getchar()) {

if(c == ‘P‘) {

V[t->v = ++cnt] = t;

} else if(c == ‘B‘) {

t = t->par;

} else {

if(!t->ch[C(c)]) {

pt->par = t;

pt->d = n++;

t->ch[C(c)] = pt++;

}

t = t->ch[C(c)];

}

}

scanf("%d", &qn);

for(int i = 0; i < qn; i++)

q[i].Read(i);

sort(q, q + qn);

memset(ql, 0, sizeof(int) * (cnt + 1));

memset(qr, -1, sizeof(int) * (cnt + 1));

for(int i = 0; i < qn; i++) {

if(!i || q[i - 1].y != q[i].y)

ql[q[i].y] = i;

if(i + 1 == qn || q[i + 1].y != q[i].y)

qr[q[i].y] = i;

}

}

struct edge {

int to;

edge* next;

} E[maxn << 1], *Pt = E, *head[maxn];

inline void AddEdge(int u, int v) {

Pt->to = v;

Pt->next = head[u];

head[u] = Pt++;

}

queue<Node*> que;

void buildFail() {

que.push(Rt);

while(!que.empty()) {

Node* t = que.front(); que.pop();

if(t->fail)

AddEdge(t->fail->d, t->d);

for(int i = 0; i < c; i++) if(t->ch[i]) {

Node* f = t->fail;

while(f && !f->ch[i])

f = f->fail;

t->ch[i]->fail = f ? f->ch[i] : Rt;

que.push(t->ch[i]);

}

}

}

struct BIT {

int b[maxn];

BIT() {

memset(b, 0, sizeof b);

}

inline void Add(int x, int v) {

for(; x <= n; x += x & -x)

b[x] += v;

}

inline int Sum(int x) {

int ret = 0;

for(; x; x -= x & -x)

ret += b[x];

return ret;

}

inline int Query(int l, int r) {

return Sum(r) - Sum(l - 1);

}

} Bit;

void DFS(int x) {

L[x] = ++dfsn;

for(edge* e = head[x]; e; e = e->next) DFS(e->to);

R[x] = dfsn;

}

void dfsAC(Node* t) {

Bit.Add(L[t->d], 1);

if(t->v) {

for(int i = ql[t->v]; i <= qr[t->v]; i++)

ans[q[i].d] = Bit.Query(L[V[q[i].x]->d], R[V[q[i].x]->d]);

}

for(int i = 0; i < c; i++)

if(t->ch[i]) dfsAC(t->ch[i]);

Bit.Add(L[t->d], -1);

}

int main() {

Init();

buildFail();

DFS(0);

dfsAC(Rt);

for(int i = 0; i < qn; i++)

printf("%d\n", ans[i]);

return 0;

}

-------------------------------------------------------------------------------------------

2434: [Noi2011]阿狸的打字机

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 1706  Solved: 974
[Submit][Status][Discuss]

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

Source

Trie

时间: 2024-08-02 15:10:10

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )的相关文章

[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

[NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后).l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失.l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失.例如,阿狸输入aPaPBbP

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

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

bzoj 2434 [Noi2011]阿狸的打字机——AC自动机

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2434 dfs AC自动机,走过的点权值+1,回溯的时候权值-1:走到询问的 y 串的节点,看一下此时 x 串 fail 树子树和即可. #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int rdn() { int ret=0;bool fx=1;char ch

[COCI2015] Divljak - AC自动机,DFS序,树状数组,LCA

有 \(n\) 个询问串 \(S_i\),有一个初始为空的字符串集合 \(T\),接下来有 \(q\) 个操作,每次向集合中添加一个字符串,或给定 \(x\) 询问集合中有多少个字符串包含 \(S_x\) Solution 考虑对 \(S_i\) 建立 ACAM,建出 \(fail\) 树,一个点发生匹配,则需要修改它到根的链,询问就是查询一个点的值 这样需要树链剖分,我们可以差分一下,那么每次修改的就是一个点,询问的是一个子树的和 于是我们求出 \(fail\) 树的 DFS 序,用树状数组维

bzoj 2434 [Noi2011]阿狸的打字机 ac

[Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4001  Solved: 2198[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后).

【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 我们把纸上打印出来的字符串从1开始顺序编号,一直到n.打字机有一个非

[NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]

题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 不那么暴力的 我们对于所有的串建立一个AC自动机,把询问按照$y$排序,然后在AC自动机上面跑,每次跳fail更新答案 这样可以拿到70分,但是时间上限还是会$O\left(n^2\right)$左右 巧妙的优化 这道题里面,所有的模板串和文本串都在AC自动机里 那么,题目中实际是在要求什么呢?

BZOJ 3172: [Tjoi2013]单词 &&  BZOJ 2434 [Noi2011]阿狸的打字机 (施工中)

fail树 链接地址 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账… 有了这个东西,我们可以做很多事… 对于AC自动机的构造前面的文章已经讲了,而在查询的时候,有一点感觉没有说清楚: 对于x串在y串中出现,必然是在y串某个前缀的后缀与x串相同 fail指针指向与该节点表示串后缀相等的且长度最大的串(或前缀)的节点 然后,根据fail指针的原理,在查询的时候,沿着当前节点的fail指针向上查找,直到root结束

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要