luogu解题报告:P2414[NOI2011]阿狸的打字机

https://www.luogu.org/problem/show?pid=2414

题目背景

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。

题目描述

打字机上只有28个按键,分别印有26个小写英文字母和’B’、’P’两个字母。经阿狸研究发现,这个打字机是这样工作的:

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

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

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

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

a aa ab

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

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

输入输出样例

输入样例#1:

aPaPBbP
3
1 2
1 3
2 3

输出样例#1:

2
1
0

说明

对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000。

思路与解

AC自动机+dfs序+树状数组。

由于涉及多个串,不难想到用AC自动机处理。考虑构建了Fail指针后形成的fail树。对于一个询问 (x,y),也就是在fail树中y的子树中寻找x的前缀。然而在fail树中维护每一个串的前缀是不可能的,因而只能反其道而行之。

在dfs遍历Trie的过程中维护当前链是容易的(即在一个树状数组中使得为1的元素是当前链上的)。当我们处理到节点y时,对于任何一个询问 (x,y),只需要查看x的子树中1的个数,后者可以用dfs序处理。复杂度为 O(LΣ+(L+N)lgL)

Code

#include <bits/stdc++.h>
using namespace std;

char str[100005];

//#define Debug

struct graph {
    struct node {
        int to, next, id;
    } edge[100005];
    int head[100005], top;
    int L[100005], R[100005], d;
    graph():top(0) { memset(head, 0, sizeof head); d = 0; }
    void push(int i, int j, int id = 0)
    {
        edge[++top].to = j;
        edge[top].next = head[i];
        edge[top].id = id;
        head[i] = top;
    }
    void dfs(int nd = 1)
    {
        L[nd] = ++d;
        for (int i = head[nd]; i; i = edge[i].next) dfs(edge[i].to);
        R[nd] = d;
    }
} query;

struct acm {
    struct node {
        int chl[26];
        int finished, fail, fa;
        node() {memset(chl, 0, sizeof chl); }
    } tree[100005];
    int top, root;
    queue<int> que;
    int str2pos[100005];
    graph failTree;
    acm():top(1), root(1){}
    void init(const char *str)
    {
        int nd = root;
        int cnt = 0;
        for (; *str != '\0'; ++str) {
            if (*str == 'P') tree[nd].finished = ++cnt, str2pos[cnt] = nd;
            else if (*str == 'B') nd = tree[nd].fa;
            else {
                tree[nd].chl[*str-'a'] = ++top;
                tree[top].fa = nd;
                nd = top;
            }
        }
    }
    void buildFail()
    {
        tree[root].fail = 0;
        for (que.push(root); !que.empty(); que.pop()) {
            int k = que.front();
            for (int i = 0; i < 26; i++) if (tree[k].chl[i]) {
                int t = tree[k].fail;
                while (t && !tree[t].chl[i]) t = tree[t].fail;
                tree[tree[k].chl[i]].fail = t?tree[t].chl[i]:root;
                failTree.push(tree[tree[k].chl[i]].fail, tree[k].chl[i]);
                que.push(tree[k].chl[i]);
            }
        }
        failTree.dfs();
    }
    void dfs(int nd, char c, int tab = 0)
    {
        if (!nd) return;
        for (int i = 1; i <= tab; i++) putchar(' ');
        cout << nd << "-->" << c << "(" << tree[nd].finished << "), fail = " << tree[nd].fail << endl;
        for (int i = 0; i < 26; i++) dfs(tree[nd].chl[i], i+'a', tab+2);
    }
} Acm;

struct bit {
    int c[100005];
    bit(){memset(c, 0, sizeof c);}
    inline int lowbit(int i) {return i&-i;}
    void modify(int i, int j)
    { for (; i < 100005; i += lowbit(i)) c[i] += j; }
    int sum(int i)
    {
        int ans = 0;
        for (; i; i -= lowbit(i)) ans += c[i];
        return ans;
    }
    inline int sum(int l, int r)
    { return l <= r ? sum(r)-sum(l-1) : 0; }
} Bit;

int x[100005], y[100005], n, ans[100005];
void init()
{
    scanf("%s", str);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x[i], &y[i]);
        query.push(y[i], x[i], i);
    }
    Acm.init(str);
    Acm.buildFail();
#ifdef Debug
    Acm.dfs(1, 'R');
#endif
}

void solve(int nd)
{
#ifdef Debug
    cout << "Solving: " << nd << endl;
#endif
    Bit.modify(Acm.failTree.L[nd], 1);
    if (Acm.tree[nd].finished) {
        for (int i = query.head[Acm.tree[nd].finished]; i; i = query.edge[i].next) {
            int to = Acm.str2pos[query.edge[i].to], id = query.edge[i].id;
#ifdef Debug
            cout << "--From : " << to << " ; id = " << id << endl;
#endif
            ans[id] = Bit.sum(Acm.failTree.L[to], Acm.failTree.R[to]);
        }
    }
    for (int i = 0; i < 26; i++)
        if (Acm.tree[nd].chl[i])
            solve(Acm.tree[nd].chl[i]);
    Bit.modify(Acm.failTree.L[nd], -1);
}

int main()
{
    freopen("noi2011_type.in", "r", stdin);
    freopen("noi2011_type.out", "w", stdout);
    init();
    solve(1);
    for (int i = 1; i <= n; i++)
        printf("%d\n", ans[i]);
    return 0;
}
posted @ 2017-02-19 10:19  ljt12138  阅读(235)  评论(0编辑  收藏  举报