BZOJ2434 NOI2011 洛谷2414 阿狸的打字机

6 篇文章 0 订阅
2 篇文章 0 订阅

题目背景

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。

题目描述

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

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

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

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

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

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

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

输入输出格式

输入格式:

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数m,表示询问个数。

接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。

输出格式:

输出m行,其中第i行包含一个整数,表示第i个询问的答案。

输入输出样例

输入样例#1:
aPaPBbP
3
1 2
1 3
2 3
输出样例#1:
2
1
0

说明

数据范围:

对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000。

花了大半天才A掉这题(洛谷上A了,BZOJ蜜汁RE,还是太菜)。

对输入的字符串构一个AC自动机,遇到P的时候标记一下节点,遇到B的时候往上退到爸爸那儿。

对于每一个询问(x,y),如果从根节点到y的路径上有一个节点的fail能连到x,说明x在y中出现了一次。只需要从根节点到y每个点都跑一次,就可以暴力出(x,y)的结果。

但是这样肯定会超时,咋办?

我们考虑根据fail函数,把每条失配边反过来建一个树,再根据这个树搞一个dfs序列。记录一下跑到每个点和跑出每个点的时间戳。

这样有啥用?

根据dfs序列的性质,序列中跑入和跑出这个点的一大坨就是这个点的儿子们。

我们对dfs序列做一个树状数组。那么我们遍历一下Tire,每跑到一个点就给这个点在序列中出现的地方+1(s),回去之后再-1(s)。跑到一个标记过的结点(到P),以这个点为(y),根据询问中所有的x,把x出现到跑出的那一段和求出来即可。

代码巨臭。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100010
#define size 26
int bit[maxn];
char s[maxn];
int head[maxn];
struct Edge{
    int next,to;
}edges[maxn];
int b_cnt=0;
void addedge(int u,int v)
{
    edges[++b_cnt].next=head[u];
    edges[b_cnt].to=v;
    head[u]=b_cnt;
    return ;
}
Edge e2[maxn];
int h1[maxn];
int lowbit(int x)
{
    return x&-x;
}
int cnt;
int l[maxn],r[maxn];
void add(int x,int val)
{
    while (x<=cnt) {
        bit[x]+=val;
        x+=lowbit(x);
    }
}
int query(int x)
{
    int ret=0;
    while (x>0)
    {
        ret+=bit[x];
        x-=lowbit(x);
    }
    return ret;
}
int ans[maxn];
struct AcAuto{
        int ch[maxn][size];
        int father[maxn];
        int val[maxn];
        int pos[maxn];
        int tot;
        int idx(char c)
        {
            return c-'a';
        }
        void build(char *s)
        {
            int len=strlen(s);
            int u=0,id=0;
            for (int i=0;i<len;i++)
            {
                if (s[i]=='P')
                {
                    pos[++id]=u;//标记打印结点
                    val[u]=1;
                }       else
                if (s[i]=='B')
                    u=father[u];//删除,退回到父节点
                else{
                      int c=idx(s[i]);
                      if (!ch[u][c])
                     {
                        ch[u][c]=++tot;
                        father[tot]=u;
                     }
                    u=ch[u][c];
                }
            }
        }
        int fail[maxn];
        void getFail()
        {
            queue<int> Q;
            fail[0]=0;
            for (int c=0;c<26;c++)
            {
                if (ch[0][c]) {
                    fail[ch[0][c]]=0;
                    Q.push(ch[0][c]);
                               }
            }
            while (!Q.empty())
            {
                int u=Q.front();Q.pop();
                for (int i=0;i<26;i++)
                    if (ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i],Q.push(ch[u][i]);
                else ch[u][i]=ch[fail[u]][i];
            }
        }
        void work(char *s)
        {
            int u=0;
            int id=0;
            int now=0;
            for (int i=0;i<strlen(s);i++)
            {
                if (s[i]=='P')
                {
                   id++;
                   for (int y=h1[id];y;y=e2[y].next)    
                   {
                           int t=pos[e2[y].to];
                           ans[y]=query(r[t])-query(l[t]-1);
                   }
                }      else
                if(s[i]=='B')  add(l[u],-1),u=father[u];
                else u=ch[u][idx(s[i])],add(l[u],1);
            }
        }
}ac;
int dfs_clock;
void dfs(int now)
{
     dfs_clock++;
     l[now]=dfs_clock;
     for (int i=head[now];i;i=edges[i].next)
     {
         dfs(edges[i].to);
     }
     r[now]=dfs_clock;
}
int main()
{
    scanf("%s",s);
    ac.build(s);
    int m;
    ac.getFail();
    dfs_clock=-1;
    for (int i=1;i<=ac.tot;i++) addedge(ac.fail[i],i);
    dfs(0);
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d",&x);
        scanf("%d",&y);
        e2[i].next =h1[y];
        e2[i].to=x;
        h1[y]=i;
    }
    memset(bit,0,sizeof(bit));
    cnt=dfs_clock;
    ac.work(s);
    for (int i=1;i<=m;i++)
      cout<<ans[i]<<endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值