【BZOJ2434】【NOI2011】阿狸的打字机(AC自动机及Fail树的性质,树状数组)

Description

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和’B’、’P’两个字母。
经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
- 按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。
- 按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a
aa
ab

我们把纸上打印出来的字符串从 1 开始顺序编号,一直到 n 。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数 ( x , y ) (其中 1 x , y n ),打字机会显示第 x 个打印的字符串在第 y 个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?


Solution

首先对于题中所给的三个操作,对应在Trie上分别是:
- 小写字母 -> 走到当前节点的该字母的子节点(如果没有就新建一个)。
- P -> 打一个结束标记
- B -> 返回父节点
我们先根据上面的规则建出题目所给串对应的AC自动机。

AC自动机的fail树的性质:
如果自动机中的两个串 p , s 满足 p s 中出现了 k 次,那么 k 等于 p 串结束点在Fail树上的子树中有多少个节点属于串 s

有了这个性质,我们可以先求出Fail树的dfn
对于每次询问,按 y 排序,在输入的串上面走一遍,每走到一个点就将其dfn插入树状数组,每离开一个点就将其dfn在树状数组上删掉。如果走到了询问的 y ,我们查询树状数组中有多少个点再 y 对应节点的子树的dfn范围内即可。


Code

/************************************************
 * Au: Hany01
 * Date: May 5th, 2018
 * Prob: [BZOJ2434][NOI2011] 阿狸的打字机
 * Email: [email protected]
************************************************/

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) ((int)(a).size())
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define Mod (1000000007)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia

template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read()
{
    register int _, __; register char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

////////////////////////Statement////////////////////////////
const int maxn = 100005;

int n, Ans[maxn], dfn[maxn], efn[maxn], tot, cnt, now, len, beg[maxn], nex[maxn], v[maxn], e, clk, pos[maxn];
char s[maxn];

//////////////////Aho-Corasick Automaton/////////////////////

struct Node
{
    int ch[26], fa, fail;
}t[maxn];

inline void buildTrie()
{
    static int u = 0;
    rep(i, len)
        if (s[i] == 'B') u = t[u].fa;
        else if (s[i] != 'P') {
            register int c = s[i] - 97;
            if (!t[u].ch[c]) t[u].ch[c] = ++ tot, t[tot].fa = u;
            u = t[u].ch[c];
        } else pos[++ cnt] = u;
    cnt = 0;
}

inline void getFail()
{
    static queue<int> q;
    rep(i, 26) if (t[0].ch[i]) q.push(t[0].ch[i]);
    while (!q.empty()) {
        register int u = q.front(), v, w; q.pop();
        rep(i, 26) if (v = t[u].ch[i]) {
            w = t[u].fail;
            while (w && !t[w].ch[i]) w = t[w].fail;
            t[v].fail = t[w].ch[i], q.push(v);
        }
    }
}

/////////////////////Build Tree//////////////////////////////

inline void add(int uu, int vv) { v[++ e] = vv, nex[e] = beg[uu], beg[uu] = e; }

void getdfn(int u)
{
    dfn[u] = ++ clk;
    for (register int i = beg[u]; i; i = nex[i]) getdfn(v[i]);
    efn[u] = clk;
}

////////////////////Answer Questions/////////////////////////

struct Question
{
    int p, s, id;
    bool operator < (const Question& A) const { return s < A.s; };
}Q[maxn];

struct FenwickTree
{
    int c[maxn];

#define lb(x) ((x) & -(x))

    inline void update(int x, int dt) {
        for ( ; x <= clk; x += lb(x)) c[x] += dt;
    }

    inline int query(int x) {
        register int Ans = 0;
        for ( ; x; x -= lb(x)) Ans += c[x];
        return Ans;
    }

    inline int query(int l, int r) { return query(r) - query(l - 1); }

}FT;

inline void Solve()
{
    static int u = 0;
    rep(i, len) {
        if (s[i] == 'B') FT.update(dfn[u], -1), u = t[u].fa;
        else if (s[i] == 'P') {
            ++ cnt;
            while (cnt == Q[now].s)
                Ans[Q[now].id] = FT.query(dfn[pos[Q[now].p]], efn[pos[Q[now].p]]), ++ now;
        } else u = t[u].ch[s[i] - 97], FT.update(dfn[u], 1);
    }
}

//////////////////////Main Function//////////////////////////

int main()
{
#ifdef hany01
    File("bzoj2434");
#endif

    scanf("%s", s), len = strlen(s);
    buildTrie(), getFail();

    For(i, 1, tot) add(t[i].fail, i);
    getdfn(0);

    n = read();
    For(i, 1, n) Q[i].p = read(), Q[i].s = read(), Q[i].id = i;
    sort(Q + 1, Q + 1 + n);

    now = 1, Solve();
    For(i, 1, n) printf("%d\n", Ans[i]);

    return 0;
}
//岭上逢久别者又别
//唐代 权德舆
//十年曾一别,征路此相逢。
//马首向何处?夕阳千万峰。 

猜你喜欢

转载自blog.csdn.net/hhaannyyii/article/details/80208951