阿狸的打字机

余生长醉 提交于 2020-01-10 23:10:28

题目描述

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 2828 个按键,分别印有 2626 个小写英文字母和 BP 两个字母。经阿狸研究发现,这个打字机是这样工作的:

  • 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
  • 按一下印有 B 的按键,打字机凹槽中最后一个字母会消失。
  • 按一下印有 P 的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

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

a
aa
ab

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

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

题解

 直接暴力显然是不行的,这里我们要利用AC自动机一些优良的性质

我们发现,一个AC自动机上的点A,它的fail指向B,那B对应的字符串一定是A的子串,同时我们发现把每个点的fail和它自己连边,就形成了一棵有根树

我们建出fail树后,问题就变成了y字符串对应的节点有多少个在x末尾对应的节点的子树内

那我们对树记录dfs序,用树状数组记录子树权值和就可以了

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<queue>
  6 #include<vector>
  7 #define fi first
  8 #define se second
  9 #define get(x) (x-'a')
 10 using namespace std;
 11 int read()
 12 {
 13     int k=0,f=1;char c=getchar();
 14     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
 15     for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f;
 16 }
 17 const int N=200055;
 18 typedef pair<int,int> P;
 19 int n,m,sum,ans[N];
 20 int cnt,ch[N][26],pos[N],fail[N];
 21 int tot,fa[N],to[N],nextt[N],head[N];
 22 int dfn,in[N],out[N];
 23 int val[N];
 24 char s[N];
 25 vector<P> V[N];
 26 queue<int> q;
 27 void build()
 28 {
 29     for(int i=0;i<26;i++)
 30         if(ch[0][i]) q.push(ch[0][i]);
 31     while(!q.empty())
 32     {
 33         int u=q.front();q.pop();
 34         for(int i=0;i<26;i++)
 35         {
 36             if(!ch[u][i]) ch[u][i]=ch[fail[u]][i];
 37             else fail[ch[u][i]]=ch[fail[u]][i],q.push(ch[u][i]);
 38         }
 39     }
 40 }
 41 void add(int a,int b)
 42 {
 43     to[++tot]=b;
 44     nextt[tot]=head[a];
 45     head[a]=tot;
 46 }
 47 void dfs(int u)
 48 {
 49     in[u]=++dfn;
 50     for(int i=head[u];i;i=nextt[i])
 51         dfs(to[i]);
 52     out[u]=dfn;
 53 }
 54 void ad(int a,int x) {for(;a<=dfn;a+=a&-a) val[a]+=x;}
 55 int query(int a) {int ans=0;for(;a;a-=a&-a) ans+=val[a];return ans;}
 56 int main()
 57 {
 58     scanf("%s",s+1);
 59     n=strlen(s+1);
 60     int now=0;
 61     for(int i=1;i<=n;i++)
 62     {
 63         if(s[i]>='a'&&s[i]<='z')    
 64         {
 65             if(!ch[now][get(s[i])]) cnt++,fa[cnt]=now,ch[now][get(s[i])]=cnt;
 66             now=ch[now][get(s[i])];
 67         }
 68         else if(s[i]=='P') pos[++sum]=cnt;
 69         else now=fa[now];
 70     }
 71     build();
 72     for(int i=1;i<=cnt;i++) 
 73         add(fail[i],i);
 74     dfs(0);
 75     m=read();
 76     for(int i=1;i<=m;i++)
 77     {
 78         int x,y;
 79         x=read();y=read();    
 80         V[y].push_back(P(x,i));
 81     }
 82     now=0;sum=0;
 83     for(int i=1;i<=n;i++)
 84     {
 85         if(s[i]>='a'&&s[i]<='z')
 86         {
 87             now=ch[now][get(s[i])];
 88             ad(in[now],1);
 89         }
 90         else if(s[i]=='B')
 91         {
 92             ad(in[now],-1);
 93             now=fa[now];
 94         }
 95         else 
 96         {
 97             sum++;
 98             for(int j=0;j<V[sum].size();j++)
 99                 ans[V[sum][j].se]+=query(out[pos[V[sum][j].fi]])-query(in[pos[V[sum][j].fi]]-1);
100         }
101     }
102     for(int i=1;i<=m;i++)
103         printf("%d\n",ans[i]);
104     return 0;
105 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!