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

    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
     

     
    emmmmmm...
    这个可能是AC自动机入门的必做题???
    反正窝貌似对AC自动机的理解更进一步了
    我们考虑只有一个询问怎么做,询问$x$串在$y$串中出现过几次。
    我们可以在$trie$树上的根到$y$的节点上暴跳$fail$,然后统计$x$节点被经过了几次。
    我们可以这样考虑,其实上面求的东西就是在$fail$树上,$x$所代表的节点的子树中有多少个$y$的节点。
    $fail$树是指$fail$指针构成的树。
    所以我们可以求出自动机上每个节点的$dfs$序。
    然后我们$dfs$ $Trie$树,访问一个节点的时候在$dfs$序上+1,在回溯的时候,把它的$dfs$序的位置-1,这样保证有且仅有一个串被打上了标记,这样就可以直接回答询问了。
    然后如果我们跳到某一个单词$i$的结尾,就把$y = i$的所有询问回答了,答案就是他子树中的标记的个数和。
    这个用树状数组就可以轻松解决了。
    我在建立$fail$指针会破坏原先$trie$树的结构,所以需要备份一份原来的树。
    然后就做完了。
     

     
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <vector>
    using namespace std;
    #define reg register
    inline char gc() {
        static const int bs = 1 << 22;
        static unsigned char buf[bs], *st, *ed;
        if (st == ed) ed = buf + fread(st = buf, 1, bs, stdin);
        return st == ed ? EOF : *st++;
    }
    #define gc getchar
    inline int read() {
        int res=0;char ch=gc();bool fu=0;
        while(!isdigit(ch))fu|=(ch=='-'), ch=gc();
        while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc();
        return fu?-res:res;
    }
    
    char s[200005];
    int T;
    struct Query {
        int x, y, id;
    }Que[200005];
    int Ans[200005];
    vector <int> ve[200005];
    
    struct FailTree {
        int nxt, to;
    }ed[200005];
    int head[200005], cnt;
    inline void add(int x, int y) {
        ed[++cnt] = (FailTree) {head[x], y};
        head[x] = cnt;
    }
    
    int n, nxt[200005][27], End[200005], Father[200005], fail[200005], where[200005], tot;
    int cpy[200005][27];
    
    inline void AC_Ins() 
    {
        int len = strlen(s + 1);
        int now = 0;
        for (reg int i = 1 ; i <= len ; i ++)
        {
            if (s[i] == 'B') now = Father[now];
            else if (s[i] == 'P') End[now] = ++n, where[n] = now;
            else {
                if (!nxt[now][s[i] - 'a']) nxt[now][s[i] - 'a'] = ++tot, Father[nxt[now][s[i] - 'a']] = now;
                now = nxt[now][s[i] - 'a'];
            }
        }
    }
    
    inline void AC_Build() 
    {
        queue <int> q;
        for (reg int i = 0 ; i < 26 ; i ++) if (nxt[0][i]) q.push(nxt[0][i]);
        while(!q.empty()) {
            int x = q.front();q.pop();
            add(fail[x], x);
            for (reg int i = 0 ; i < 26 ; i ++) {
                if (nxt[x][i]) fail[nxt[x][i]] = nxt[fail[x]][i], q.push(nxt[x][i]);
                else nxt[x][i] = nxt[fail[x]][i];
            }
        }
    }
    
    int in[200005], out[200005];
    int Tim;
    
    void dfs1(int x) 
    {
        in[x] = ++Tim;
        for (reg int i = head[x] ; i ; i = ed[i].nxt)
            dfs1(ed[i].to);
        out[x] = Tim;
    }
    
    int tr[200005];
    inline void Modify(int x, int y) {
        while(x <= Tim) {
            tr[x] += y;
            x += x & -x;
        }
    }
    inline int Ask(int x) {
        int res = 0;
        while(x) {
            res += tr[x];
            x -= x & -x;
        }
        return res;
    }
    int reply = 1; 
    void dfs2(int x) 
    {
        Modify(in[x], 1);
        for (reg int i = 0 ; i < (signed)ve[End[x]].size() ; i ++)
            Ans[ve[End[x]][i]] = Ask(out[where[Que[ve[End[x]][i]].x]]) - Ask(in[where[Que[ve[End[x]][i]].x]] - 1);
        for (reg int i = 0 ; i <= 26 ; i ++)
            if (cpy[x][i]) dfs2(cpy[x][i]);
        Modify(in[x], -1);
    }
    
    int main()
    {
        scanf("%s", s + 1);
        AC_Ins();
        for (reg int i = 0 ; i <= tot ; i ++)
            for (reg int j = 0 ; j < 26 ; j ++)
                cpy[i][j] = nxt[i][j];
        AC_Build();
        dfs1(0);
        T = read();
        for (reg int i = 1 ; i <= T ; i ++)
        {
            Que[i].x = read(), Que[i].y = read();
            Que[i].id = i;
            ve[Que[i].y].push_back(i);
        }
        dfs2(0);
        for (reg int i = 1 ; i <= T ; i ++) printf("%d
    ", Ans[i]);
        return 0;
    }
  • 相关阅读:
    STL——increment/decrement/dereference操作符
    STL——静态常量整数成员在class内部直接初始化
    STL——临时对象的产生与运用
    C++中模板类使用友元模板函数
    模板编程中的技巧
    plsql 表数据中文显示乱码(配置环境变量)
    plsql 常用快捷键(自动替换)
    javascript 跳出(终止)forEach循环
    plsql 快捷键配置
    plsql oracle client没有正确安装(plsql连接远程数据库)
  • 原文地址:https://www.cnblogs.com/BriMon/p/9752605.html
Copyright © 2011-2022 走看看