• 洛谷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
    ", ans[i]);
        }
    }
    
  • 相关阅读:
    制作Windows Server 2008安装启动U盘
    wireshark教程(一)
    TCPdump抓包命令详解
    ATM交换机 和普通交换机区别
    胖ap和瘦ap区别
    酒店网络非常常见故障一例
    JQuery EasyUI DataGrid动态合并(标题)单元) 一
    字典表左右选择
    treegrid-dnd.js
    MySQL开发规范
  • 原文地址:https://www.cnblogs.com/AlphaWA/p/10877870.html
Copyright © 2020-2023  润新知