bzoj2434 [Noi2011]阿狸的打字机

link

 

题意:

给一个长为n的操作字符串,3种操作:

  1. 小写字母:表示在末尾插入一个字符
  2. 'B':表示删除末尾字符
  3. 'P':表示产生一个串

接下来m个询问,给出x,y问第x个串在第y个串中出现了几次。

$n,m\leq 10^5.$

 

题解:

ac自动机每个节点代表着字符串的一个前缀,注意到fail树的性质:每个节点所代表的串为其儿子所代表的串的后缀。x串在y串中出现的次数可以转化为:判断y的所有前缀的后缀是否为x。那么放到fail树上就是,x串的末尾节点的子树中有多少个y串节点。子树问题用dfs序转化为区间问题,如果对于询问离线后按照y排序,那么问题变成加/删字符、进行上述询问。直接树状数组维护。

另外值得一提的是,打字机的性质非常好,我们只需对原串建一个自动机,遇到'B'就跳回father,遇到'P'把节点编号加到队列里即可。

复杂度$\mathcal{O}(n\log n).$

 

code:

 1 #include<bits/stdc++.h>
 2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
 3 #define ll long long
 4 #define inf 1000000001
 5 #define y1 y1___
 6 using namespace std;
 7 char gc(){
 8     static char buf[100000],*p1=buf,*p2=buf;
 9     return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
10 }
11 #define gc getchar
12 ll read(){
13     char ch=gc();ll x=0;int op=1;
14     for (;!isdigit(ch);ch=gc()) if (ch=='-') op=-1;
15     for (;isdigit(ch);ch=gc()) x=(x<<1)+(x<<3)+ch-'0';
16     return x*op;
17 }
18 #define N 100005
19 int n,m,tot,num,clk,cnt,head[N],c[N][26],fa[N],fail[N],q[N],ans[N],in[N],out[N];char str[N];
20 struct node{int x,y,id;}a[N];
21 bool cmp(node x,node y){return x.y<y.y;}
22 struct edge{int to,nxt;}e[N<<1];
23 void adde(int x,int y){
24     e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt;
25 }
26 struct bit{
27     int t[N];
28     bit(){memset(t,0,sizeof(t));}
29     void add(int x,int val){
30         for (int i=x;i<=tot+1;i+=i&-i) t[i]+=val;//注意是tot+1!
31     }
32     int qry(int x){
33         int ret=0;
34         for (int i=x;i;i-=i&-i) ret+=t[i];
35         return ret;
36     }
37 }tr;
38 void init(){
39     int now=0;
40     rep (i,1,n) if ('a'<=str[i]&&str[i]<='z'){
41         int &tmp=c[now][str[i]-'a'];
42         if (!tmp) tmp=++tot;
43         fa[tmp]=now;
44         now=tmp;
45     } else if (str[i]=='P') q[++num]=now;//第num个串的节点编号
46     else now=fa[now];
47 }
48 void build(){
49     queue<int> q;
50     rep (i,0,25) if (c[0][i]) adde(0,c[0][i]),q.push(c[0][i]);
51     while (!q.empty()){
52         int u=q.front();q.pop();
53         rep (i,0,25) if (c[u][i]){
54             fail[c[u][i]]=c[fail[u]][i];
55             adde(fail[c[u][i]],c[u][i]);
56             q.push(c[u][i]);
57         } else c[u][i]=c[fail[u]][i];
58     }
59 }
60 void dfs(int u,int pr){
61     in[u]=++clk;
62     for (int i=head[u];i;i=e[i].nxt)
63         if (e[i].to!=pr) dfs(e[i].to,u);
64     out[u]=clk;
65 }
66 int main(){
67     scanf("%s",str+1);n=strlen(str+1);
68     init();build();dfs(0,-1);
69     m=read();
70     rep (i,1,m) a[i].x=read(),a[i].y=read(),a[i].id=i;
71     sort(&a[1],&a[m+1],cmp);
72     int now=0,j=1,k=0;
73     rep (i,1,n){//x节点的fail树子树中y串的节点数量
74         if ('a'<=str[i]&&str[i]<='z'){
75             now=c[now][str[i]-'a'];
76             tr.add(in[now],1);
77         } else if (str[i]=='B'){
78             tr.add(in[now],-1);
79             now=fa[now];
80         } else if (++k==a[j].y){
81             while (j<=m&&a[j].y==k){
82                 int x=q[a[j].x];
83                 ans[a[j].id]=tr.qry(out[x])-tr.qry(in[x]-1);
84                 j++;
85             }
86         }
87     }
88     rep (i,1,m) printf("%d\n",ans[i]);
89     return 0;
90 }
View Code

 

易错:

见代码注释。

posted @ 2018-07-28 20:59  bestfy  阅读(180)  评论(0编辑  收藏  举报