【Bzoj 2434】[NOI 2011] 阿狸的打字机

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

题解

经典的AC自动机+fail树+dfs序+树状数组,考试中会有点难想到用dfs序,写起来还是爽的飞起的

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <ctime>
#include <set>

using namespace std;

#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 100005
#define inf 100000007
typedef long long ll;
priority_queue<int >QwQ;

int l1,l=0,m,top=1,last=0,tot=1,cnt=0;
int c[INF<<1],to[INF<<1],h[INF<<1],s[INF<<1],p[INF],fa[INF],t[INF][30],q[INF],fail[INF];
int ans[INF],L[INF],R[INF];
char s1[INF];
int read()
{
    int k=0,f=1;
    char ch;
    while(ch<'0' || ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
    return k*f;
}
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int v)
{
    int i,j,k;
    for(i=x;i<=cnt;i+=lowbit(i))c[i]+=v;
}
int getsum(int x)
{
    int i,j,k=0;
    for(i=x;i;i-=lowbit(i))k+=c[i];
    return k;
}
void hah(int x,int y)
{
    to[++l]=y,h[l]=s[x],s[x]=l;
}
void Insert()
{
    int i,j,k,now=1,id=0;
    rep(i,0,l1-1){
        if(s1[i]=='P')p[++id]=now;
        else if(s1[i]=='B')now=fa[now];
        else {
            k=s1[i]-'a'+1;
            if(!t[now][k])t[now][k]=++tot,fa[tot]=now;
            now=t[now][k];
        }
    }
}
void build_fail()
{
    int i,j,k,p;
    q[0]=1,fail[1]=0;
    while(last<top){
        k=q[last++];
        rep(i,1,26)
         if(t[k][i]){
            p=fail[k];
            while(!t[p][i])p=fail[p];
            fail[t[k][i]]=t[p][i];
            q[top++]=t[k][i];
         }
    }
}
void dfs(int x)
{
    int i,j,k;
    L[x]=++cnt;
    for(i=s[x];i;i=h[i])
     dfs(to[i]);
    R[x]=++cnt;
}
void solve()
{
    int i,j,k,now=1,id=0;
    add(L[1],1);
    rep(i,0,l1-1){
        if(s1[i]=='P'){
            id++;
            for(j=s[id];j;j=h[j]){
                k=p[to[j]];
                ans[j]=getsum(R[k])-getsum(L[k]-1);
            }
        }
        else if(s1[i]=='B')add(L[now],-1),now=fa[now];
        else now=t[now][s1[i]-'a'+1],add(L[now],1);
    }
}
void init()
{
    int i,j,k,x,y;
    tot=1;
    rep(i,1,26)t[0][i]=1;
    scanf("%s",s1);
    l1=strlen(s1);
    Insert();
    build_fail();
    rep(i,1,tot)hah(fail[i],i);
    dfs(0);
    l=0;
    memset(s,0,sizeof(s));
    m=read();
    rep(i,1,m){
        x=read(),y=read();
        hah(y,x);
    }
    solve();
    rep(i,1,m)printf("%d\n",ans[i]);
}
void work()
{
    int i,j,k;
}
int main()
{
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值