zoukankan      html  css  js  c++  java
  • BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)

    题目这么说的:

    阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
    经阿狸研究发现,这个打字机是这样工作的:

    1. 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
    2. 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
    3. 按一下印有'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序+线段树实现。
    • 而对于这题的具体实现:
      1. 首先可以顺着输入的字符串序列B往父亲走,P标记这样建立AC自动机
      2. 然后把查询离线处理,用邻接表记录每一组查询<x,y>中每个y有哪几个x
      3. 最后再顺着输入的字符串序列,遇到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 }
  • 相关阅读:
    关键C函数备录
    TCP/UDP编程步骤和区别
    Pro C/C++环境搭建
    linux常用命令和选项
    运行在linux上的mysql常用命令
    把指针作为形参,用于取值的用法
    Leetcode476.Number Complement数字的补数
    Leetcode463.Island Perimeter岛屿的周长
    Leetcode461Hamming Distance汉明距离
    Leetcode455.Assign Cookies分发饼干
  • 原文地址:https://www.cnblogs.com/WABoss/p/5407253.html
Copyright © 2011-2022 走看看