bzoj2434 [Noi2011]阿狸的打字机
题意:
给一个长为n的操作字符串,3种操作:
- 小写字母:表示在末尾插入一个字符
- 'B':表示删除末尾字符
- '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 }
易错:
见代码注释。