洛谷2414(构建ac自动机fail树dfs序后遍历Trie树维护bit及询问答案)

要点

  • 这是一道蔡队题,看我标题行事
  • 任意询问y串上有多少个x串,暴力找每个节点是不是结尾肯定是炸的,考虑本质:如果某节点是x的结尾,根据ac自动机的性质,x一定是此(子)串后缀。又有每个Trie节点的fail只指向另一个节点,故有fail树的概念。问题就变成了“对于串x的尾节点,在fail树中它的子树中有多少个点是在y串上”
  • 解决方法是巧妙的。
  • 离线记录查询的信息。然后搜索原Trie树,遇到尾节点就扫描它有哪些查询,这里尾节点是y的尾节点。而当前搜索时如果我们在搜该点,则该点计数++,搜完它的子树回溯了,该点计数--,这样做使得搜到尾节点时,只有这个字符串上的节点才有计数,达到了想要的效果:只有串y的节点才有计数。
  • 那么现在y上的所有节点都被计数了,怎样统计有多少个是在x的fail子树上呢?就是在之前预处理dfs序,子树的常规操作。这样计数是在dfn上进行的,维护和查询用一下树状数组即可,想查询x的子树有多少值就直接查询前缀和即可。
  • 注意除了思路以外还有写法上的优化,就题论板子,比如这题常规地insert就会T,发现题目特殊性质可以特殊插入,大大加快了速度。
  • 总的来讲虽然标题很花哨但是操作都是中规中矩的,需要什么映射的数组就开一下就是了。其实没处理鲁棒性使得一些数据能hack掉我的代码,比如空串还删或者出现相同的串,但没想到A了那就懒得改了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
#include <map>
using namespace std;

const int N = 1e5 + 5;

char op[N], t[N];
int m, cnt;
vector<pair<int, int>> query[N];
int trie[N][26], dfn[N], size[N], Time;
int x, y, ans[N];

int ch[N][26];//Trie树的转移
int fa[N];
int val[N];//根据题意赋值。有值则意味着某子串末尾
int fail[N];//失配,转移到别的树枝接着找
int sz;//注意这个板子sz一定是要从1开计
int book[N];
vector<int> ftr[N];

void getfail() {
    queue<int> Q;
    for (int i = 0; i < 26; i++)
        if (ch[0][i]) {
            fail[ch[0][i]] = 0;
            ftr[0].push_back(ch[0][i]);
            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]){
                fail[ch[u][i]] = ch[fail[u]][i];
                if (val[ch[u][i]] != -1)
                    ftr[ch[fail[u]][i]].push_back(ch[u][i]);
                Q.push(ch[u][i]);//指向其他枝上同样的字母
            } else  ch[u][i] = ch[fail[u]][i];//使得find时半路突然失配时还能一下拐回去
    }
}

void dfs(int now) {
    dfn[now] = ++Time;
    size[now] = 1;
    for (int i : ftr[now]) {
        dfs(i);
        size[now] += size[i];
    }
}

struct BIT {
    int F[N];

    void add(int x, int val) {
        for (; x <= Time; x += x&-x)
            F[x] += val;
    }

    int ask(int x) {
        int res = 0;
        for (; x; x -= x&-x)
            res += F[x];
        return res;
    }
}bit;

void Dfs(int cur) {
    int y = val[cur];
    bit.add(dfn[cur], 1);
    if (y != -1) {
        for (auto i : query[y]) {
            int sz = book[i.first];
            ans[i.second] = bit.ask(dfn[sz] + size[sz] - 1) - bit.ask(dfn[sz] - 1);
        }
    }
    for (int i = 0; i < 26; i++) {
        if (trie[cur][i])
            Dfs(trie[cur][i]);
    }
    bit.add(dfn[cur], -1);
}

int main() {    
    memset(val, -1, sizeof val);
    scanf("%s", op);
    int L = strlen(op), now = 0;
    for (int i = 0; i < L; i++) {
        if (op[i] == 'B') {
            now = fa[now];
        } else if (op[i] == 'P') {
            val[now] = ++cnt;
            book[cnt] = now;
        } else {
            if (!ch[now][op[i] - 'a']) {
                trie[now][op[i] - 'a'] = ch[now][op[i] - 'a'] = ++sz;
                val[sz] = 0;
                fa[sz] = now;
            }
            now = ch[now][op[i] - 'a'];
        }
    }

    getfail();
    dfs(0);
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d %d", &x, &y);
        query[y].push_back({x, i});
    }
    Dfs(0);
    for (int i = 1; i <= m; i++) {
        printf("%d\n", ans[i]);
    }
}

转载于:https://www.cnblogs.com/AlphaWA/p/10877870.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值