[NOI 2011]阿狸的打字機

Description

題庫連接php

給你 \(n\) 個單詞, \(m\) 組詢問,每組詢問形同 \((x,y)\) ,詢問 \(x\) 串在 \(y\) 串中出現多少次。c++

\(1\leq n,m\leq10^5\)git

Solution

比較暴力的作法就是建好 \(AC\) 自動機後,對於每一個 \(y\) 串暴力跑一遍。看看查詢的時候有多少次落在了 \(x\) 串的末尾。數組

咱們能夠構建 \(fail\) 樹,那麼其實題目能夠轉變爲對於 \(x\) 串末尾節點,其子樹中有多少個節點位於 \(y\) 串上。ui

因爲題目的特殊性,咱們能夠離線詢問按照 \(y\) 來排序。而且預處理出 \(AC\) 自動機的 \(dfn\)spa

咱們按照構建 \(Trie\) 樹的操做再按原字符串走一遍。入棧時對應的 \(dfn\)\(+1\) ,出時對應的 \(dfn\)\(-1\) 。那麼走到一個單詞節點,全部打上標記的 \(dfn\) 都是該單詞上的。code

注意到一個子樹內的 \(dfn\) 都是連續的,顯然就能夠回答全部 \(y\) 等於該單詞的詢問了。樹狀數組維護 \(dfn\) 的標記的前綴和便可。排序

Code

//It is made by Awson on 2018.3.18
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('\n'))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 100000;
void read(int &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}
void print(int x) {if (x > 9) print(x/10); putchar(x%10+48); }
void write(int x) {if (x < 0) putchar('-'); print(Abs(x)); }

char ch[N+5];
int n, m, idx, dfn[N+5], size[N+5], ans[N+5], mp[N+5];
struct tt {int to, next; }edge[(N<<1)+5];
struct qu {
    int x, y, id;
    bool operator < (const qu &b) const {return y < b.y; }
}que[N+5];
int path[N+5], top;
void add(int u, int v) {edge[++top].to = v, edge[top].next = path[u], path[u] = top; }
struct bittree {
    int c[N+5];
    void add(int o, int val) {for (; o <= idx; o += lowbit(o)) c[o] += val; }
    int count(int o) {int ans = 0; for (; o; o -= lowbit(o)) ans += c[o]; return ans; }
}BT;
struct Trie {
    int ch[N+5][26], pre[N+5], f[N+5], val[N+5], pos;
    void build(char *S) {
    int u = 0;
    for (int i = 0, len = strlen(S); i < len; i++) {
        if (S[i] == 'P') {val[u] = ++n, mp[n] = u; continue; }
        if (S[i] == 'B') {u = pre[u]; continue; }
        if (ch[u][S[i]-'a'] == 0) ++pos, pre[pos] = u, ch[u][S[i]-'a'] = pos;
        u = ch[u][S[i]-'a'];
    }
    }
    void get_fail() {
    queue<int>Q;
    for (int i = 0; i < 26; i++) if (ch[0][i]) f[ch[0][i]] = 0, Q.push(ch[0][i]);
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        for (int i = 0; i < 26; i++) {
        if (ch[u][i]) f[ch[u][i]] = ch[f[u]][i], Q.push(ch[u][i]);
        else ch[u][i] = ch[f[u]][i];
        }
    }
    for (int i = 1; i <= pos; i++) add(f[i], i);
    }
    void query(char *S) {
    int loc = 1, u = 0;
    for (int i = 0, len = strlen(S); i < len; i++) {
        if (S[i] == 'P') {
        while (loc <= m && que[loc].y == val[u])
            ans[que[loc].id] = BT.count(dfn[mp[que[loc].x]]+size[mp[que[loc].x]]-1)-BT.count(dfn[mp[que[loc].x]]-1), ++loc;
        }else if (S[i] == 'B') BT.add(dfn[u], -1), u = pre[u];
        else u = ch[u][S[i]-'a'], BT.add(dfn[u], 1);
    }
    }
}T;
void dfs(int o) {
    size[o] = 1, dfn[o] = ++idx;
    for (int i = path[o]; i; i = edge[i].next) {
    dfs(edge[i].to); size[o] += size[edge[i].to];
    }
}

void work() {
    scanf("%s", ch); T.build(ch); T.get_fail();
    dfs(0); read(m);
    for (int i = 1; i <= m; i++) read(que[i].x), read(que[i].y), que[i].id = i;
    sort(que+1, que+1+m); T.query(ch);
    for (int i = 1; i <= m; i++) writeln(ans[i]);
}
int main() {
    work(); return 0;
}
相關文章
相關標籤/搜索