bzoj2434 [Noi2011]阿狸的打字机

http://www.elijahqi.win/archives/2884
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
逗号表达式竟然又一次出锅?why以前用的好好的 今天垃圾蒟蒻我调试半天
考虑m*l做法怎么做 很显然就是把所有的串建在一棵trie上 然后针对每个串我去跑一遍在那个串上有多少个是符合条件的
那么如果字符串A可以有fail指向B那么显然 B将会是A的一个后缀 那么可以知道我A包含了B 那么 假如A串中有n个fail指向了B 那么显然 B就在A 中出现了n次 直接按照这个思路做 复杂度仍然太高怎么办 考虑将我们的fail边反向 那么显然因为fail只有一个父节点 那么我这个反向的边建出来的一定是一棵树 那么我们将会获得一个离线的做法 怎么办首先将所有的询问离线进来 按照询问的y从小到大排序 那么我将整个操作序列也离线进来 然后每次移动的时候如果串的个数增加了且恰好等于我当前询问的y 那么我会把询问这个y的x全部都做一遍 具体怎么做考虑用树状数组维护dfs序 我首先标出dfs序列 然后每次做的时候把当前节点的都+1表示我这个如果询问我的子树我可以快速ologn的得到答案 如果遇到了P 那么就打印,然后加一个字符串 如果遇到了B 就删除 然后跳到trie树上的fa 继续
注意:对于每个x串,只有能通过fail指针指向它的末尾节点的y串节点才能计数 统计的话因为是fail建树直接统计子树是没问题的

#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
struct node{
    int y,next;
}data[N];
char s[N];int cnt=1,S[N],num,h[N],ed[N],fa[N],trans[N][26],fail[N],in[N],out[N],m,ans[N];
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x;
}int nn;
inline void add(int x,int v){while(x<=nn) S[x]+=v,x+=x&-x;}
inline int query(int x){int tmp=0;while(x) tmp+=S[x],x-=x&-x;return tmp;}
inline void insert1(int x,int y){
    data[++num].y=y;data[num].next=h[x];h[x]=num;
}
inline int build(){
    int len=strlen(s+1);int p=1;static int nxt;
    for (int i=1;i<=len;++i){
        if(s[i]=='P') {ed[++num]=p;continue;}
        if(s[i]=='B') {p=fa[p];continue;}
        if (!trans[p][s[i]-'a']) trans[p][s[i]-'a']=nxt=++cnt;
        else nxt=trans[p][s[i]-'a'];fa[nxt]=p;p=nxt;
    }return len;
}
inline void buildAC(){
    num=0;queue<int>q;q.push(1);for (int i=0;i<26;++i) trans[0][i]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=0;i<26;++i){
            int &y=trans[x][i];
            if (y) fail[y]=trans[fail[x]][i],q.push(y);else
            {y=trans[fail[x]][i];continue;}insert1(fail[y],y);
        }
    }
}
struct node1{
    int x,y,id;
}qr[N];
inline bool cmp(const node1 &a,const node1 &b){return a.y<b.y;}
inline void dfs(int x){
    in[x]=++num;for (int i=h[x];i;i=data[i].next) dfs(data[i].y);out[x]=num;
}
int main(){
    freopen("bzoj2434.in","r",stdin);
    scanf("%s",s+1);int len=build();buildAC();m=read();
    for (int i=1;i<=m;++i)qr[i].x=read(),qr[i].y=read(),qr[i].id=i;
    sort(qr+1,qr+m+1,cmp);num=0;dfs(1);int p=1,now=1;nn=cnt;cnt=0;
    for (int i=1;i<=len;++i){
        if (s[i]=='B'){add(in[p],-1);p=fa[p];continue;}
        if (s[i]=='P'){++cnt;int x;
            while(qr[now].y==cnt&&now<=m) 
            {x=ed[qr[now].x];
            ans[qr[now].id]=query(out[x])-query(in[x]-1);++now;}
            continue;
        }p=trans[p][s[i]-'a'];add(in[p],1);
    }for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值