阿狸的打字机
作者:互联网
题目描述
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 2828 个按键,分别印有 2626 个小写英文字母和 B
、P
两个字母。经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- 按一下印有
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 }
标签:阿狸,int,字母,打字机,凹槽,fail,include 来源: https://www.cnblogs.com/waing/p/12178465.html