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

BZOJ2434:[NOI2011]阿狸的打字机——题解

时间:2018-05-06 12:20:12      阅读:163      评论:0      收藏:0      [点我收藏+]

标签:log   href   span   作者   scanf   个数   +++   研究   bzoj2434   

https://www.lydsy.com/JudgeOnline/problem.php?id=2434

https://www.luogu.org/problemnew/show/P2414

打字机上只有28个按键,分别印有26个小写英文字母和‘B‘、‘P‘两个字母。经阿狸研究发现,这个打字机是这样工作的:

·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

·按一下印有‘B‘的按键,打字机凹槽中最后一个字母会消失。

·按一下印有‘P‘的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a aa ab 我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

因为没有前置技能现学了一发fail树,再回头看就是fail树棵题了。

因为询问x串在y串出现几次,可以直接转化为y串在trie的节点在fail树x串末节点的子树中出现了几次(这个比较绕,但是很好想。)

而且因为这题特殊的建trie的方式,我们完全可以离线,对y排序,然后重新跑一遍建trie的过程,这样就保证了y的快(顺)速(序)查找。

那么中间记录y节点的工作就交给树状数组完成即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int S=1e5+5;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch==-;ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct trie{
    int a[26],fa,fail;
}tr[S];
struct node{
    int to,nxt;
}e[S];
struct data{
    int x,y,id;
}p[S];
int m,tot,cnt,ed[S],id,head[S];
int idx,pos[S],a[S],size[S],ans[S];
char s[S];
queue<int>q;
inline bool cmp(data a,data b){
    return a.y<b.y;
}
inline void add(int u,int v){
    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
inline int lowbit(int x){return x&(-x);}
inline void ins(int x,int y){
    for(int i=x;i<=idx;i+=lowbit(i))a[i]+=y;
}
inline int qry(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i))res+=a[i];
    return res;
}
void build(){
    int n=strlen(s),now=0;
    for(int i=0;i<n;i++){
        if(s[i]==P)ed[++id]=now;
        else if(s[i]==B)now=tr[now].fa;
        else{
            int c=s[i]-a;
            if(!tr[now].a[c])tr[now].a[c]=++tot;
            tr[tr[now].a[c]].fa=now;
            now=tr[now].a[c];
        }
    }
}
void dfs_trie(){
    int n=strlen(s),now=0,r=0,j=1,sum=0;
    for(int i=0;i<n;i++){
        if(s[i]==P){
            sum++;
            for(;j<=m;j++){
                if(p[j].y==sum){
                    int l=pos[ed[p[j].x]],r=l+size[ed[p[j].x]]-1;
                    ans[p[j].id]=qry(r)-qry(l-1);
                }else break;
            }
        }
        else if(s[i]==B){
            ins(pos[now],-1);
            now=tr[now].fa;
        }
        else{
            now=tr[now].a[s[i]-a];
            ins(pos[now],1);
        }
    }
}
void getfail(){
    tr[0].fail=0;
    for(int i=0;i<26;i++){
        int u=tr[0].a[i];
        if(u){
            tr[u].fail=0;
            q.push(u);
            add(0,u);
        }
    }
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[u].a[i]){
                int v=tr[u].a[i];
                tr[v].fail=tr[tr[u].fail].a[i];
                q.push(v);
                add(tr[v].fail,v);
            }else tr[u].a[i]=tr[tr[u].fail].a[i];
        }
    }
    return;
}
void dfs_fail(int u){
    pos[u]=++idx;size[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        dfs_fail(v);
        size[u]+=size[v];
    }
}
int main(){
    scanf("%s",s);
    build();getfail();dfs_fail(0);
    m=read();
    for(int i=1;i<=m;i++){
        p[i].x=read(),p[i].y=read(),p[i].id=i;
    }
    sort(p+1,p+m+1,cmp);
    dfs_trie();
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +

+++++++++++++++++++++++++++++++++++++++++++

BZOJ2434:[NOI2011]阿狸的打字机——题解

标签:log   href   span   作者   scanf   个数   +++   研究   bzoj2434   

原文地址:https://www.cnblogs.com/luyouqi233/p/8997583.html

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