• 【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)


    Description

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

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

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

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

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

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

    a

    aa

    ab

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

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

    Input

     输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

    第二行包含一个整数m,表示询问个数。

    接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。

    Output

     输出m行,其中第i行包含一个整数,表示第i个询问的答案。

    Sample Input

    aPaPBbP

    3

    1 2

    1 3

    2 3

    Sample Output

    2

    1

    0

    HINT

     1<=N<=10^5


    1<=M<=10^5

    输入总长<=10^5

    【分析】

    •    这道题是用AC自动机里的fail指针连成的树做的。
    •   用到fail树。
    •   首先有一个朴素算法就是找到第y个单词在trie树上的路径然后沿着每一个点的fail指针走,如果找到x就加1(想想fail指针建立的过程)。
    •   由此可以运用逆向思维,以x为根的子树沿着fail指针倒着走能找到多少个y路径上的点就说明x在y上出现过几次。每次都dfs找一遍,用树状数组维护,这样可以得到70分。
    •   同时这是一个离线算法,一遍dfs,遇到一个结束标记,就做一下这个串的询问,插一个点在树状数组+1,离开这个点时-1。
    •   对于树上每个点只插入一次,时间复杂度就得到了保证。

    有一个很详细的题解:http://blog.csdn.net/huzecong/article/details/7769988

    代码如下:(丑)

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 #define Maxn 100100
      9 #define Maxl 100010
     10 
     11 char s[Maxl];
     12 int p[Maxn];
     13 
     14 struct hp
     15 {
     16     int x,y,next,id,ans;
     17 }qy[Maxn];int al;
     18 
     19 struct node
     20 {
     21     int son[30],cnt,fail;
     22     int num,rt,fa,st;
     23 }t[Maxn];int tot;
     24 
     25 int first[Maxn],c[Maxn];
     26 
     27 bool cmp(hp x,hp y) {return p[x.y]<p[y.y];}
     28 bool cmp2(hp x,hp y) {return x.id<y.id;}
     29 
     30 void ins(int x,int y)
     31 {
     32     qy[++al].x=x;qy[al].y=y;
     33     qy[al].next=first[x];first[x]=al;
     34 }
     35 
     36 void upd(int x,int f)
     37 {
     38     t[x].cnt=0;t[x].fa=f;t[x].st=0;
     39     memset(t[x].son,0,sizeof(t[x].son));
     40 }
     41 
     42 void read_trie()
     43 {
     44     scanf("%s",s+1);
     45     int len=strlen(s+1);
     46     memset(p,0,sizeof(p));
     47     int now=0;int ql=0;tot=0;
     48     for(int j=1;j<=len;j++)
     49     {
     50         if(s[j]>='a'&&s[j]<='z')
     51         {
     52             int ind=s[j]-'a'+1;
     53             if(!t[now].son[ind]) 
     54              t[now].son[ind]=++tot,upd(tot,now); 
     55             now=t[now].son[ind];
     56             if(j==len) t[now].cnt++;
     57         }
     58         else if(s[j]=='B') now=t[now].fa;
     59         else p[++ql]=now;
     60     }
     61 }
     62 
     63 queue<int > q;
     64 void build_AC()
     65 {
     66     int i,j,x,y;
     67     while(!q.empty()) q.pop();
     68     q.push(0);
     69     while(!q.empty())
     70     {
     71         x=q.front();
     72         y=t[x].fail;
     73         for(j=1;j<=26;j++)
     74         {
     75             if(t[x].son[j])
     76             {
     77                 q.push(t[x].son[j]);
     78                 t[t[x].son[j]].fail=x?t[y].son[j]:x;
     79                 ins(t[t[x].son[j]].fail,t[x].son[j]);
     80             }
     81             else t[x].son[j]=t[y].son[j];
     82         }
     83         q.pop();
     84     }
     85 }
     86 
     87 void dfs(int x)
     88 {
     89     t[x].num=++al;t[x].rt=t[x].num;
     90     for(int i=first[x];i;i=qy[i].next)
     91       dfs(qy[i].y),t[x].rt=t[qy[i].y].rt;
     92 }
     93 
     94 void add(int x,int y)
     95 {
     96     for(int i=x;i<=tot+1;i+=i&(-i))
     97      c[i]+=y;
     98 }
     99 
    100 int getsum(int x)
    101 {
    102     int ans=0;
    103     for(int i=x;i>=1;i-=i&(-i))
    104         ans+=c[i];
    105     return ans;
    106 }
    107 
    108 void dfs2(int x)
    109 {
    110     add(t[x].num,1);
    111     if(t[x].st!=0)
    112     {
    113         for(int i=t[x].st;;i++)
    114         {
    115             if(p[qy[i].y]!=x) break;
    116             qy[i].ans=getsum(t[p[qy[i].x]].rt)-getsum(t[p[qy[i].x]].num-1);
    117         }
    118     }
    119     for(int i=1;i<=26;i++) if(t[x].son[i]&&t[t[x].son[i]].fa==x)
    120     {
    121         dfs2(t[x].son[i]);
    122     }
    123     add(t[x].num,-1);
    124 }
    125 
    126 void init()
    127 {
    128     memset(first,0,sizeof(first));
    129     al=0;
    130     read_trie();
    131     build_AC();al=0;
    132     dfs(0);
    133     int m;
    134     scanf("%d",&m);
    135     for(int i=1;i<=m;i++) {scanf("%d%d",&qy[i].x,&qy[i].y);qy[i].id=i;}
    136     sort(qy+1,qy+1+m,cmp);
    137     qy[0].y=0;
    138     for(int i=1;i<=m;i++) 
    139       if(p[qy[i].y]!=p[qy[i-1].y]) t[p[qy[i].y]].st=i;
    140       else t[p[qy[i].y]].st=t[p[qy[i-1].y]].st;
    141     memset(c,0,sizeof(c));
    142     dfs2(0);
    143     sort(qy+1,qy+1+m,cmp2);
    144     for(int i=1;i<=m;i++) printf("%d
    ",qy[i].ans);
    145 }
    146 
    147 int main()
    148 {
    149     init();
    150     return 0;
    151 }
    [BZOJ2434]

    2016-06-16 16:55:20

  • 相关阅读:
    win10+vs2010 安装Silverlight 安装说明
    常用小方法
    .net 技术学习进阶
    NetMQ——推拉模式 Push-Pull
    NetMQ使用——发布订阅模式 Publisher-Subscriber
    NetMQ使用——请求响应模式 Request-Reply
    跨终端Web
    七大排序算法
    JDK线程池
    Redis为什么这么快
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/5591468.html
Copyright © 2020-2023  润新知