题目这么说的:
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
- 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
- 把AC自动机的结点作为树结点,将fail指针的方向取反设为树边,这样除了AC自动机头结点外每个结点都有且仅有一条进入它的边,这样就得到一棵叫fail树的树。
- 在AC自动机中如果y的fail指针指向x,可知x表示的字符串前缀是y结点表示的字符串前缀的后缀;因此可以知道在fail树中x为根的子树的所有结点表示的字符串前缀都包含x结点所表示的字符串前缀。
- 对于,询问一个字符串x在另一个字符串y出现了多少次,就可以这样:在fail树中标记字符串y的所有前缀结点,然后看x为根的子树有多少个被标记结点就是答案,这个可以用DFS序+线段树实现。
- 而对于这题的具体实现:
- 首先可以顺着输入的字符串序列B往父亲走,P标记这样建立AC自动机
- 然后把查询离线处理,用邻接表记录每一组查询<x,y>中每个y有哪几个x
- 最后再顺着输入的字符串序列,遇到B和小写字符更新线段树,遇到P去查询出当前y的所有x的答案
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 #define MAXN 111111 7 8 struct Edge{ 9 int v,next; 10 }edge[MAXN]; 11 int NE,head[MAXN]; 12 void addEdge(int u,int v){ 13 edge[NE].v=v; edge[NE].next=head[u]; 14 head[u]=NE++; 15 } 16 17 int tn,ch[MAXN][26],fa[MAXN],flag[MAXN],fail[MAXN]; 18 int belong[MAXN],bn,rbelong[MAXN]; 19 char str[111111]; 20 void insert(){ 21 int x=0; 22 for(int i=0; str[i]; ++i){ 23 if(str[i]=='B'){ 24 x=fa[x]; 25 }else if(str[i]=='P'){ 26 flag[x]=1; 27 belong[x]=++bn; 28 rbelong[bn]=x; 29 }else{ 30 int y=str[i]-'a'; 31 if(ch[x][y]==0) ch[x][y]=++tn,fa[ch[x][y]]=x; 32 x=ch[x][y]; 33 } 34 } 35 } 36 void getfail(){ 37 queue<int> que; 38 for(int i=0; i<26; ++i){ 39 if(ch[0][i]){ 40 que.push(ch[0][i]); 41 addEdge(0,ch[0][i]); 42 } 43 } 44 while(!que.empty()){ 45 int x=que.front(); que.pop(); 46 for(int i=0; i<26; ++i){ 47 if(ch[x][i]){ 48 que.push(ch[x][i]); 49 fail[ch[x][i]]=ch[fail[x]][i]; 50 addEdge(fail[ch[x][i]],ch[x][i]); 51 }else ch[x][i]=ch[fail[x]][i]; 52 } 53 } 54 } 55 56 int dfn,l[MAXN],r[MAXN],par[MAXN]; 57 void dfs(int u){ 58 l[u]=++dfn; 59 for(int i=head[u]; i!=-1; i=edge[i].next){ 60 int v=edge[i].v; 61 par[v]=u; 62 dfs(v); 63 } 64 r[u]=dfn; 65 } 66 67 int tree[MAXN<<2],N,x,y; 68 void update(int i,int j,int k){ 69 if(i==j){ 70 tree[k]+=y; 71 return; 72 } 73 int mid=i+j>>1; 74 if(x<=mid) update(i,mid,k<<1); 75 else update(mid+1,j,k<<1|1); 76 tree[k]=tree[k<<1]+tree[k<<1|1]; 77 } 78 int query(int i,int j,int k){ 79 if(x<=i && j<=y){ 80 return tree[k]; 81 } 82 int mid=i+j>>1,res=0; 83 if(x<=mid) res+=query(i,mid,k<<1); 84 if(y>mid) res+=query(mid+1,j,k<<1|1); 85 return res; 86 } 87 88 struct Query{ 89 int x,y,ans,next; 90 }que[MAXN]; 91 int qhead[MAXN],qNE,order[MAXN]; 92 void addQuery(int x,int y){ 93 que[qNE].x=x; que[qNE].y=y; que[qNE].next=qhead[y]; 94 qhead[y]=qNE++; 95 } 96 97 void doit(){ 98 int now=0; 99 for(int i=0; str[i]; ++i){ 100 if(str[i]=='B'){ 101 x=l[now]; y=-1; 102 update(1,N,1); 103 now=fa[now]; 104 }else if(str[i]=='P'){ 105 for(int j=qhead[belong[now]]; j!=-1; j=que[j].next){ 106 x=l[rbelong[que[j].x]]; y=r[rbelong[que[j].x]]; 107 que[j].ans=query(1,N,1); 108 } 109 }else{ 110 now=ch[now][str[i]-'a']; 111 x=l[now]; y=1; 112 update(1,N,1); 113 } 114 } 115 } 116 int main(){ 117 scanf("%s",str); 118 insert(); 119 memset(head,-1,sizeof(head)); 120 getfail(); 121 dfs(0); 122 for(N=1; N<dfn; N<<=1); 123 memset(qhead,-1,sizeof(qhead)); 124 int m,a,b; 125 scanf("%d",&m); 126 for(int i=0; i<m; ++i){ 127 scanf("%d%d",&a,&b); 128 order[i]=qNE; 129 addQuery(a,b); 130 } 131 doit(); 132 for(int i=0; i<m; ++i){ 133 printf("%d ",que[order[i]].ans); 134 } 135 return 0; 136 }