码迷,mamicode.com
首页 > 其他好文 > 详细

阿狸的打字机(bzoj 2434)

时间:2017-03-14 22:59:25      阅读:260      评论:0      收藏:0      [点我收藏+]

标签:nod   add   pre   输出   front   names   字符   std   nbsp   

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树的题目。
    fail树就是根据AC自动机上的fail指针建的树,树边由fail[i]指向i。
    查询x在y中出现的次数就是在fail树中查询y的子树中x的节点出现的次数。
    用树状数组维护一下。 
*/
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#define N 100010
using namespace std;
int l[N],r[N],t[N*2],n,m,cnt,T,size=1;
int head[N],headq[N],pos[N],ans[N];
int fa[N],a[N][26],point[N];
char ch[N];
queue<int> q;
struct node{
    int v,pre;
};node e[N],eq[N];

void insert(int u,int v){
    e[++cnt].v=v;e[cnt].pre=head[u];head[u]=cnt;
}
void add(int x,int v){
    for(int i=x;i<=T;i+=i&-i) t[i]+=v;
}
int query(int x){
    int sum=0;
    for(int i=x;i;i-=i&-i) sum+=t[i];
    return sum;
}
void trie(){
    int now=1,id=0;
    for(int i=0;i<n;i++)
        if(ch[i]==P) pos[++id]=now;
        else if(ch[i]==B) now=fa[now];
        else {
            int t=ch[i]-a;
            if(!a[now][t]){
                a[now][t]=++size;
                fa[a[now][t]]=now;
            }
            now=a[now][t];
        }
}
void build(){
    q.push(1);point[1]=0;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(!a[now][i]) continue;
            int k=point[now];
            while(!a[k][i]) k=point[k];
            point[a[now][i]]=a[k][i];
            q.push(a[now][i]);
        }
    }
}
void solve(){
    int now=1,id=0;
    add(l[1],1);
    for(int i=0;i<n;i++)
        if(ch[i]==P){
            id++;
            for(int x=headq[id];x;x=eq[x].pre){
                int t=pos[eq[x].v];
                ans[x]=query(r[t])-query(l[t]-1);
            }
        }
        else if(ch[i]==B) add(l[now],-1),now=fa[now];
        else now=a[now][ch[i]-a],add(l[now],1);
}
void dfs(int x){
    l[x]=++T;
    for(int i=head[x];i;i=e[i].pre)
        dfs(e[i].v);
    r[x]=++T;
}
int main(){
    scanf("%s",ch);
    n=strlen(ch);
    for(int i=0;i<26;i++) a[0][i]=1;
    trie();build();
    for(int i=1;i<=size;i++)
        insert(point[i],i);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        eq[i].v=x;
        eq[i].pre=headq[y];
        headq[y]=i;
    }
    dfs(0);
    solve();
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}

 

阿狸的打字机(bzoj 2434)

标签:nod   add   pre   输出   front   names   字符   std   nbsp   

原文地址:http://www.cnblogs.com/harden/p/6551073.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!