zoukankan      html  css  js  c++  java
  • bzoj2434

    ac自动机+bit

    很早以前就做过这道题了,再做一遍。

    构建ac自动机的话就是模拟一下就可以了,然后就是如何统计答案。

    这里巧妙地利用了fail树的性质,fail树是指当前在trie上从根到这个节点的路径,也就是某个单词的前缀,这个单词的前缀的后缀能够匹配上另一个单词的前缀,于是就把fail指针连向那个单词的前缀最后的节点。

    fail树的根就是ac自动机的根。

    如果我们用一个模式串来匹配单词,那么fail指针就能够统计当前前缀的后缀的单词的数量,如果不能继续匹配下去,就沿着fail指针走。

    这道题利用了fail树的性质,统计x在y中出现多少次,就是说从根到y的路径中有多少个节点的fail指向x,也就是说有多少位置的后缀是x单词,fail指针总是指向一个是当前后缀的前缀,而前缀就是一个单词。

    直接暴力是不行的,那么我们对于询问分类,把每个x用邻接表存在对应的y下,然后求出fail树的dfs序,按照建自动机的方式模拟,每插入完一个单词,就统计这个单词对应的询问,统计询问是利用树状数组,每走到一个节点就把这个节点的dfs序插入bit,离开就删除,这样就保证bit里总是只存在当前的单词,走到打印的地方时,这个单词的所有节点就都插入在bit里了,也不会有其他多余的单词,所以每次统计询问单词的子树和就行了,因为我们是查询y单词有多少节点的fail指针指向x,那么把fail树反向,也就是说有多少y单词上的节点在x的子树里,因为fail指针能够构成一棵树,并且是有方向的,只有一个单词的后缀指向前缀才会后构成fail指针,所以对于一个单词,fail指针的反向也就统计了一个单词出现在其他单词的次数。

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int, int> PII;
    const int N = 100010;
    int n, m;
    vector<int> g[N];
    vector<PII> G[N];
    int ans[N];
    char s[N];
    struct BIT {
        int tree[N];
        int lowbit(int i) { return i & (-i); }
        void update(int pos, int delta)
        {
            for(int i = pos; i <= n; i += lowbit(i)) tree[i] += delta;
        }
        int query(int pos)
        {
            int ret = 0;
            for(int i = pos; i; i -= lowbit(i)) ret += tree[i];
            return ret;
        }
    } t;
    struct AC {
        int root, Time, tot, cnt;
        int child[N][26], fail[N], pos[N], mir[N], st[N], fi[N], fa[N];
        void construct(char s[])
        {
            int now = root;
            for(int i = 1; i <= n; ++i)
            {
                if(s[i] >= 'a' && s[i] <= 'z')
                {
                    if(!child[now][s[i] - 'a'])
                        child[now][s[i] - 'a'] = ++cnt;
                    fa[child[now][s[i] - 'a']] = now;
                    now = child[now][s[i] - 'a'];
                }
                else if(s[i] == 'B') now = fa[now];
                else 
                {
                    pos[now] = ++tot;
                    mir[tot] = now;
                }
            }
        }
        void get_fail()
        {
            queue<int> q;
            q.push(root);
            while(!q.empty())
            {
                int u = q.front();
                q.pop();
                for(int i = 0; i < 26; ++i) if(child[u][i])
                {
                    int v = child[u][i];
                    if(u == root) fail[v] = root;
                    else
                    {
                        int now = fail[u];
                        while(now != root && !child[now][i]) now = fail[now];
                        fail[v] = child[now][i];
                    }
                    g[v].push_back(fail[v]);
                    g[fail[v]].push_back(v);
                    q.push(v);
                }
            }
        }
        void solve()
        {
            int now = root;
            for(int i = 1; i <= n; ++i)
            {
                if(s[i] == 'P')
                {
                    for(int j = 0; j < G[pos[now]].size(); ++j)
                    {
                        PII x = G[pos[now]][j];
                        int u = mir[x.first], id = x.second;
                        ans[id] += t.query(fi[u]) - t.query(st[u] - 1); 
                    }
                } 
                else if(s[i] == 'B') 
                {
                    t.update(st[now], -1);
                    now = fa[now];
                }
                else
                {
                    now = child[now][s[i] - 'a'];
                    t.update(st[now], 1);
                }
            }
        }
        void dfs(int u, int last)
        {
            st[u] = ++Time;
            for(int i = 0; i < g[u].size(); ++i)
            {
                int v = g[u][i];
                if(v == last) continue;
                dfs(v, u);
            }
            fi[u] = Time;
        }
    } ac;
    int main()
    {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        ac.construct(s);
        ac.get_fail();
        ac.dfs(0, -1);
        scanf("%d", &m);
        for(int i = 1; i <= m; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            G[y].push_back(make_pair(x, i));
        }
        ac.solve();
        for(int i = 1; i <= m; ++i) printf("%d
    ", ans[i]);
        return 0;
    } 
    View Code
  • 相关阅读:
    mysql数据库
    Mysql之sql语句操作
    mysql修改root密码的多种方法
    kvm虚拟化
    清华AIOps算法:KPI聚类
    有点扯的预测方法
    内网安全运营的逻辑体系架构
    SpringBoot定时消费Kafka消息
    kafka的consumer消费能力很低的情况下的处理方案
    Kafka_Kafka 消费者 偏移量 与 积压 查询脚本 kafka-consumer-groups.sh
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7222204.html
Copyright © 2011-2022 走看看