NOI2011阿狸的打字机(fail树+DFS序)

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

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

fail树是这样一个东西:将AC自动机的fail指针连成一棵树(根节点为虚拟节点0)。

这样有什么好处呢?匹配时沿着fail树向上找就能找到所有匹配位置了:

void find(char* s)
{
    int j=0,c;
    for(int i=0;s[i]!=‘\0‘;i++)
    {
        c=s[i]-‘a‘;
        while(j&&!ch[j][c]) j=f[j];
        for(int t=j;t;t=f[t]) if(val[t]) cnt[val[t]]++;
    }
}

也就是说,对于一个节点c从root(0)走向c连成的字符串(模板串),一个节点v从root(0)走向v连成的字符串(匹配串),匹配串在模板串中出现的次数就是root->c的路径上的节点有多少出现在节点v的子树中。

这样我们只需维护DFS序即可。

那么这道题的做法就水落石出了:

首先构建AC自动机的fail树得出dfs序,得出每个结点进出时间l[x],r[x],考虑这样一种暴力

对于一个询问x,y,查询自动机上root-y的每一个结点,沿着fail指针是否会走到x的结尾点,如果可以即答案+1

那么就变为在fail树中,查询自动机上root-y的所有结点中,有多少个在x的子树中。

只要在自动机上再重新走一遍,走到一个结点y,则将1-l[y]都+1。解决询问x,y(把y相同的链表串起来),即查询l[x]到r[x]的和。当遇到一个B时1-l[y]都-1。

只需树状数组实现加减和区间求和

随便写了写,竟然在BZOJ上rank1了,我很开心(<---这货二模跪了还在这里作死)

#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
inline int read()
{
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
void print(int x)
{
    if(!x) {putchar(‘0‘);putchar(‘\n‘);return;}
    int len=0,buf[15];
    while(x) buf[++len]=x%10,x/=10;
    for(int i=len;i;i--) putchar(buf[i]+‘0‘);
    putchar(‘\n‘);
}
const int maxn=200010;
int ch[maxn][26],fa[maxn],f[maxn],pos[maxn],id,sz;
void insert(char* s)
{
    int j=0,c;
    for(int i=0;s[i];i++)
    {
        c=s[i]-‘a‘;
        if(s[i]==‘P‘) pos[++id]=j;
        else if(s[i]==‘B‘) j=fa[j];
        else
        {
            if(!ch[j][c]) ch[j][c]=++sz,fa[sz]=j;
            j=ch[j][c];
        }
    }
}
queue<int> q;
void getfail()
{
    for(int c=0;c<26;c++) if(ch[0][c]) q.push(ch[0][c]);
    while(!q.empty())
    {
        int u=q.front(),v;q.pop();
        for(int c=0;c<26;c++) if(v=ch[u][c])
        {
            q.push(v);int j=f[u];
            while(j&&!ch[j][c]) j=f[j];
            f[v]=ch[j][c];
        }
    }
}
int first[maxn],next[maxn],to[maxn],e;
void AddEdge(int u,int v){to[++e]=v;next[e]=first[u];first[u]=e;}
int l[maxn],r[maxn],cnt;
void dfs(int x)
{
    l[x]=++cnt;
    for(int i=first[x];i;i=next[i]) dfs(to[i]);
    r[x]=++cnt;
}
int sumv[maxn];
void add(int x,int v) {for(;x<=cnt;x+=x&-x) sumv[x]+=v;}
int sum(int x) {int ret=0;for(;x;x-=x&-x) ret+=sumv[x];return ret;}
struct Query {int next,v;}Q[maxn];
int firstq[maxn],ans[maxn];
void solve(char* s)
{
    int j=0,c;add(l[j],1);id=0;
    for(int i=0;s[i];i++)
    {
        c=s[i]-‘a‘;
        if(s[i]==‘P‘)
        {
            id++;for(int x=firstq[id];x;x=Q[x].next)
            {
                int v=pos[Q[x].v];
                ans[x]=sum(r[v])-sum(l[v]-1);
            }
        }
        else if(s[i]==‘B‘) add(l[j],-1),j=fa[j];
        else j=ch[j][c],add(l[j],1);
    }
}
char s[maxn];
int main()
{
    scanf("%s",s);insert(s);getfail();
    for(int i=1;i<=sz;i++) AddEdge(f[i],i);
    dfs(0);
    int m=read();
    for(int i=1;i<=m;i++)
    {
       int x=read(),y=read();
       Q[i].v=x;Q[i].next=firstq[y];firstq[y]=i;
    }
    solve(s);
    for(int i=1;i<=m;i++) print(ans[i]);
    return 0;
}

时间: 2024-10-06 10:07:24

NOI2011阿狸的打字机(fail树+DFS序)的相关文章

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

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

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

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

BZOJ 2434 NOI2011 阿狸的打字机 fail树+树状数组

题目大意:初始字串为空,首先给定一系列操作序列,有三种操作: 1.在结尾加一个字符 2.在结尾删除一个字符 3.打印当前字串 然后多次询问第x个打印的字串在第y个打印的字串中出现了几次 卡了很久--到底还是对AC自动机了解不是很深啊QAQ fail树不是很难想 至少在用AC自动机切掉3172之后不是很难想-- 首先构建AC自动机 注意由于这个字串的特殊构造 我们不必每打印一个字符串再插入Trie树 令now为当前指针 初始为root 考虑三种操作 在结尾添加一个字符->新建一个子节点(若存在在不

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

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

【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组

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

bzoj 2434 阿狸的打字机 fail树的性质

如果a串是另b串的后缀,那么在trie图上沿着b的fail指针走一定可以走到a串. 而a串在b串里出现多少次就是它是多少个前缀的后缀. 所以把fail边反向建树维护个dfs序就行了. 并不是很难...但没想出来TAT 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #define N 200005 6 #define inf 0x3f3f3

【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组

E. e-Government time limit per test:1 second memory limit per test:256 megabytes input:standard input output:standard output The best programmers of Embezzland compete to develop a part of the project called "e-Government" — the system of automa

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]阿狸的打字机(fail树+离线处理+BIT)

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