zoukankan      html  css  js  c++  java
  • bzoj2434 [Noi2011]阿狸的打字机

    link

    题意:

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

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

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

    $n,mleq 10^5.$

    题解:

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

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

    复杂度$mathcal{O}(nlog 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
    ",ans[i]);
    89     return 0;
    90 }
    View Code

    易错:

    见代码注释。

  • 相关阅读:
    AC日记——[HNOI2008]GT考试 bzoj 1009
    AC日记——[SCOI2009]游戏 bzoj 1025
    AC日记——[HNOI2010]BOUNCE 弹飞绵羊 洛谷 P3203
    AC日记——旅游 bzoj 2157
    NOIP模拟2017.6.11解题报告
    AC日记——【模板】Link Cut Tree 洛谷 P3690
    AC日记——[SDOI2010]大陆争霸 洛谷 P3690
    [NOI2010]超级钢琴 倍增
    [HNOI2004]L语言 字典树 记忆化搜索
    对拍
  • 原文地址:https://www.cnblogs.com/bestFy/p/9383405.html
Copyright © 2011-2022 走看看