zoukankan      html  css  js  c++  java
  • 【bzoj2434-阿狸的打字机】AC自动机+fail树+优化

    http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23083

    Description

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

    经阿狸研究发现,这个打字机是这样工作的:

    l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

    l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。

    l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

    例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

    a

    aa

    ab

    我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。

    阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

    Sample Input

    aPaPBbP
    3
    1 2
    1 3
    2 3

    Sample Output

    2
    1
    0

    题解:这道题做了好久。。哭泣。。。。。。。

    主要是超时的问题。

    fail树:按照AC自动机上fail的反向边建立的树。

    如样例:

    可以知道:failtree上,任意一点所代表的子串(即以trie上根到该点的串)是它在fail树上的子树任意一点的子串。

    就是说,x是y的祖先,那么以y为结尾的串一直fail可以fail到x,就是x是y的子串。

    那么现在可以得出一个简单粗暴的方法:

    对于每个询问(x,y),求x在y上出现的次数,我们就可以在fail树上找到x串末尾的点A,然后询问y串在fail树上的每一个点是否是x的子树中的点,是就ans++。

    但是明显,这个方法慢出天际了。。。。

    优化:

     (三颗星)离线。在fail树上用dfn给节点编号,则每一个点的子树在节点号上是连续的一段。在trie上走一遍,每到一个点x就在树状数组的dfn[x]位置+1,每离开一个点就在树状数组的dfn[x]位置-1。然后每当走到一个串的末尾,就询问每个对应要询问的x串,给fail树上它的子树求和,就是树状数组上getsum A~next[A]-1,这样就求出了y上多少个点为末尾可以fail到x,就是该询问(x,y)的答案。

    后面就是我的错误点了:

    (1)循环字符串的时候不要打for(int i=0;i<strlen(s);i++) ... 拿个变量存一下strlen(s)。。。超慢的。。

    (2)做trietree的时候不用每次从头开始。。接着上一次的末尾就可以了。。快超多。。

    (3)最后询问的时候不要把所有询问扫一遍。。用个first存y的边目录啊。。

    血与泪啊。。

    代码如下:

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<vector>
      6 #include<queue>
      7 #include<cmath>
      8 using namespace std;
      9 
     10 const int N=100010,S=26;
     11 struct node{
     12     int k,fa,fail,son[30];
     13 }a[N];
     14 struct edge{
     15     int x,y,next,fa,ans;
     16 }b[N],t[N];
     17 char s[N];
     18 int pre,m,tl,sl,num,cnt_s,len,cnt_dfn;
     19 int last[N],c[N],sk[N],dfn[N],next[N],first[N],fir[N];
     20 queue<int> q;
     21 
     22 void clear(int x)
     23 {
     24     a[x].fa=a[x].k=a[x].fail=0;
     25     memset(a[x].son,0,sizeof(a[x].son));
     26 }
     27 
     28 void buildAC()
     29 {
     30     while(!q.empty()) q.pop();
     31     for(int i=1;i<=S;i++)
     32         if(a[0].son[i]) q.push(a[0].son[i]);
     33     while(!q.empty())
     34     {
     35         int x=q.front();q.pop();
     36         int fail=a[x].fail;
     37         for(int i=1;i<=S;i++)
     38         {
     39             if(a[x].son[i])
     40             {
     41                 a[a[x].son[i]].fail=a[fail].son[i];
     42                 q.push(a[x].son[i]);
     43             }
     44             else a[x].son[i]=a[fail].son[i];
     45         }
     46     }
     47 }
     48 
     49 void ins(int x,int y,bool bk)
     50 {
     51     if(!bk)
     52     {
     53         b[++len].x=x;b[len].y=y;
     54         b[len].next=first[x];first[x]=len;
     55     }
     56     else
     57     {
     58         t[++tl].x=x;t[tl].y=y;t[tl].ans=0;
     59         t[tl].next=fir[x];fir[x]=tl;
     60     }
     61 }
     62 
     63 void dfs(int x)
     64 {
     65     dfn[x]=++cnt_dfn;
     66     for(int i=first[x];i;i=b[i].next)
     67     {
     68         int y=b[i].y;
     69         dfs(y);
     70     }
     71     next[x]=cnt_dfn;
     72 }
     73 
     74 void build_fail_tree()
     75 {
     76     for(int i=1;i<=num;i++) ins(a[i].fail,i,0);
     77     dfs(0);
     78 }
     79 
     80 int lowbit(int x){return x&(-x);}
     81 void add(int x,int d){for(int i=x;i<=N-10;i+=lowbit(i)) c[i]+=d;}
     82 int getsum(int x)
     83 {
     84     int ans=0;
     85     for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i];
     86     return ans;
     87 }
     88 
     89 int main()
     90 {
     91     freopen("a.in","r",stdin);
     92     freopen("me.out","w",stdout);
     93     num=0;cnt_s=0;cnt_dfn=0;len=0;pre=0;tl=0;clear(0);
     94     memset(fir,0,sizeof(fir));
     95     memset(first,0,sizeof(first));
     96     memset(c,0,sizeof(c));
     97     scanf("%s",s);
     98     //build trie tree
     99     int strl=strlen(s),now=0;
    100     for(int i=0;i<strl;i++)
    101     {
    102         if(s[i]=='P') 
    103         {
    104             a[now].k=1;
    105             last[++cnt_s]=now;
    106         }
    107         else if(s[i]=='B') now=a[now].fa;
    108         else 
    109         {
    110             int ind=s[i]-'a'+1;
    111             if(!a[now].son[ind])
    112             {
    113                 num++;clear(num);
    114                 a[now].son[ind]=num;
    115                 a[num].fa=now;
    116             }
    117             now=a[now].son[ind];
    118         }
    119     }
    120     //build ac
    121     buildAC(); 
    122     //build fail tree
    123     build_fail_tree();
    124     scanf("%d",&m);
    125     for(int i=1;i<=m;i++)
    126     {
    127         int x,y;
    128         scanf("%d%d",&x,&y);
    129         ins(y,x,1);    
    130     }
    131     //find
    132     now=0,cnt_s=0;
    133     for(int i=0;i<strl;i++)
    134     {
    135         if(s[i]=='B')
    136         {
    137             add(dfn[now],-1);
    138             now=a[now].fa;
    139         }
    140         else if(s[i]=='P')
    141         {
    142             cnt_s++;
    143             for(int j=fir[cnt_s];j;j=t[j].next)
    144             {
    145                 int st=dfn[last[t[j].y]],ed=next[last[t[j].y]];
    146                 t[j].ans=getsum(ed)-getsum(st-1);
    147             }
    148         }
    149         else 
    150         {
    151             int ind=s[i]-'a'+1;
    152             now=a[now].son[ind];
    153             add(dfn[now],1);
    154         }
    155     }
    156     for(int i=1;i<=m;i++)
    157         printf("%d
    ",t[i].ans);
    158     return 0;
    159 }
  • 相关阅读:
    javascript-类型、值和变量
    词法结构
    前端js保存页面为图片下载到本地
    js生成带log的二维码(qrcodejs)
    工具函数
    css 水平垂直居中
    vue中的keep-alive
    vuex 的使用
    Spring源码解析
    Spring源码解析
  • 原文地址:https://www.cnblogs.com/KonjakJuruo/p/5662749.html
Copyright © 2011-2022 走看看