zoukankan      html  css  js  c++  java
  • 【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组

    2434: [Noi2011]阿狸的打字机

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 2022  Solved: 1158
    [Submit][Status][Discuss]

    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

    Source

    Trie

    Solution

    好题,值得多次练习

    AC自动机无疑,建出Fail指针的同时,根据Fail指针反向建树,即Fail树

    以x为根的子树沿着fail指针倒着走能找到多少个y路径上的点就说明x在y上出现过几次。

    那么利用DFS,求出每个点的时间戳(即每个节点最先到达的时间和最后到达时间),那么发现,对于子树,是一种类似前缀和的存在

    那么用线段树或者树状数组来维护一下

    值得注意的几点:(皆为亲身经历)

    首先在建Trie树的过程中,应该考虑直接对整个操作串进行处理,不断分离去插入会TLE

    对于操作串的长度,不应该直接在for循环中使用strlen(),应该先记录下来len=strlen()后循环中直接用len,否则更TLE

    不要企图在线处理,应该优先考虑离线处理,否则也会出现奇怪的错误

    对于Fail树,应该建单向边,并非双向边,否则可能出现错误

    数组要开的够大,题目中10^5,开到10^5+100,Tyvj秒A,CodeVS秒A,BZOJ狂WA不止,怒开2×10^5秒A...不知所措

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    int read()
    {
        int x=0;char ch=getchar();
        while(ch<'0'||ch>'9')ch=getchar();
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x;
    }
    #define N 210000
    int son[N][30],fail[N],q[N],sz,t,len,m,ans[N],fa[N],loc[N];
    char s[N],opt[N];
    struct data{int to,next;}edge[N],ask[N];
    int cn,cnt,head[N],heada[N],lastq[N];
    void addedge(int u,int v)
    {cnt++;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;}
    void addask(int x,int y,int z)
    {ask[z].next=heada[y];heada[y]=z;ask[z].to=x;}
    int tree[N];
    inline int lowbit(int x){return x&(-x);}
    void change(int loc,int val)
    {
        for (int i=loc; i<=t; i+=lowbit(i))
            tree[i]+=val;
    }
    int query(int loc)
    {
        int ans=0;
        for (int i=loc; i>=1; i-=lowbit(i))
            ans+=tree[i];    
        return ans;
    }
    int L[N],R[N];
    void dfs(int x)
    {
        L[x]=++t;
        for (int i=head[x]; i; i=edge[i].next)
            dfs(edge[i].to);
        R[x]=++t;
        //printf("L=%d R=%d
    ",L[x],R[x]);
    }
    void clear()
    {sz=1;for (int i=0; i<26; i++) son[0][i]=1;}
    void buildTrie()
    {
        int x=1,id=0;
        for (int i=0; i<len; i++)
            if (opt[i]=='P') loc[++id]=x;
                else if (opt[i]=='B') x=fa[x];
                    else 
                        {
                            if (!son[x][opt[i]-'a'])
                                 son[x][opt[i]-'a']=++sz,fa[sz]=x;
                             x=son[x][opt[i]-'a'];
                        }
    }
    void buildFail()
    {
        int he=0,ta=1; q[0]=1; fail[1]=0;
        while (he<ta)
            {
                int now=q[he++];
                for (int i=0; i<26; i++)
                    {
                        if (!son[now][i]) continue;
                        int ff=fail[now];
                        while (!son[ff][i]) ff=fail[ff];
                        fail[son[now][i]]=son[ff][i];
                        q[ta++]=son[now][i];
                    }
            }
        for (int i=1; i<=sz; i++)
            addedge(fail[i],i);
    }
    void work()
    {
        int now=1,id=0;
        change(L[1],1);
        for (int i=0; i<len; i++)
            if (opt[i]=='P')
                {
                    id++;
                    for (int j=heada[id]; j; j=ask[j].next)
                        ans[j]=query(R[loc[ask[j].to]])-query(L[loc[ask[j].to]]-1);
                }
            else if (opt[i]=='B') change(L[now],-1),now=fa[now];
                else now=son[now][opt[i]-'a'],change(L[now],1);
    }    
    //ACMachine
    int main()
    {    
        clear(); 
        scanf("%s",opt); len=strlen(opt);
        buildTrie(); buildFail();
        m=read();
        for (int x,y,i=1; i<=m; i++)
            x=read(),y=read(),addask(x,y,i);
        dfs(0); work();
        for (int i=1; i<=m; i++)
            printf("%d
    ",ans[i]);
        return 0;
    }

    BZOJ数组大小被坑艰辛史

  • 相关阅读:
    Ubuntu下hadoop2.4搭建集群(单机模式)
    软考历程(2)——海明码校验
    key 串口
    android删除文件出错
    ORACLE 11G没有备份文件參数文件在异机通过rman备份恢复找回被误删的数据
    【SICP归纳】2 高阶函数和数据抽象
    Java加密算法 AES
    从BAE到SAE,从SAE又回到BAE
    velocity-字母序号 list
    Java 加密解密 对称加密算法 非对称加密算法 MD5 BASE64 AES RSA
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5352189.html
Copyright © 2011-2022 走看看