• 洛谷P2414 阿狸的打字机


    AC自动机 + 树状数组

    这道题暴力把询问离线排序之后,直接用AC自动机找答案可以拿到70分。。

    正解应该是考虑fail树和trie树父亲节点的意义。

    题目是让我们求第x个单词在第y个中出现了多少次,我们先看第y个单词在trie中的意义。

    假设第y个单词在trie中以p节点结尾,那么p节点的父亲节点(包括p节点自己)到根组成的串均为y的前缀。

    而这些节点的fail指针指向的节点代表什么呢?假设p的父亲节点a他的fail指针指向b,那么意思是 以b结尾的单词是y这个单词的前缀(根到a是y这个单词的一个前缀)的一个后缀。

    前缀的后缀显然就是子串,也就是在y中出现过的串。

    那么a这个点的fail树上的祖先节点显然全在y中出现过。

    所以我们只要统计每一个y,他在trie中的父亲节点有几个是fail树中x的子节点就行啦。

    所以我们先dfs一遍fail树,把fail树映射成序列,再dfs一次trie,对经过的每个节点+1,回溯时-1,计算答案的时候,只要用树状数组辅助查询区间和就好了。

    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define full(a, b) memset(a, b, sizeof a)
    #define __fastIn ios::sync_with_stdio(false), cin.tie(0)
    #define pb push_back
    using namespace std;
    using LL = long long;
    inline int lowbit(int x){ return x & (-x); }
    inline int read(){
        int ret = 0, w = 0; char ch = 0;
        while(!isdigit(ch)){
            w |= ch == '-', ch = getchar();
        }
        while(isdigit(ch)){
            ret = (ret << 3) + (ret << 1) + (ch ^ 48);
            ch = getchar();
        }
        return w ? -ret : ret;
    }
    template <typename A>
    inline A __lcm(A a, A b){ return a / __gcd(a, b) * b; }
    template <typename A, typename B, typename C>
    inline A fpow(A x, B p, C lyd){
        A ans = 1;
        for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
        return ans;
    }
    const int N = 100005;
    string t;
    int n, tot, cnt, trie[N][26], fa[N], ending[N], fail[N], idx[N], k, head[N], dfn[N], c, size[N], tree[N];
    int x, y, ans[N], tr[N][26];
    struct Edge { int v, next; } edge[N<<1];
    vector<pair<int, int>> Q[N];
    inline void add(int k, int val){
        for(; k <= c; k += lowbit(k)) tree[k] += val;
    }
    
    inline int query(int k){
        int ret = 0;
        for(; k; k -= lowbit(k)) ret += tree[k];
        return ret;
    }
    
    void addEdge(int a, int b){
        edge[k].v = b, edge[k].next = head[a], head[a] = k ++;
    }
    
    void getFail(){
        queue<int> q;
        for(int i = 0; i < 26; i ++){
            if(trie[0][i]){
                tr[0][i] = trie[0][i];
                q.push(trie[0][i]);
            }
        }
        while(!q.empty()){
            int s = q.front(); q.pop();
            for(int i = 0; i < 26; i ++){
                if(trie[s][i]){
                    tr[s][i] = trie[s][i];
                    fail[trie[s][i]] = trie[fail[s]][i];
                    q.push(trie[s][i]);
                }
                else trie[s][i] = trie[fail[s]][i];
            }
        }
    }
    
    void dfs(int s, int fa){
        dfn[s] = ++ c, size[s] = 1;
        for(int i = head[s]; i != -1; i = edge[i].next){
            int u = edge[i].v;
            if(u == fa) continue;
            dfs(u, s);
            size[s] += size[u];
        }
    }
    
    void match(int s){
        add(dfn[s], 1);
        if(ending[s]){
            for(auto &p: Q[s]){
                int x = p.first, y = p.second;
                ans[y] = query(dfn[x] + size[x] - 1) - query(dfn[x] - 1);
            }
        }
        for(int i = 0; i < 26; i ++){
            if(tr[s][i]) match(tr[s][i]);
        }
        add(dfn[s], -1);
    }
    
    int main(){
    
        __fastIn;
        cin >> t >> n;
        int p = 0;
        for(int i = 0; i < t.size(); i ++){
            if(t[i] >= 'a' && t[i] <= 'z'){
                int ch = t[i] - 'a';
                if(!trie[p][ch]) trie[p][ch] = ++ tot, fa[tot] = p;
                p = trie[p][ch];
            }
            else if(t[i] == 'B') p = fa[p];
            else ending[p] = ++cnt, idx[cnt] = p;
        }
        getFail();
        full(head, -1);
        for(int i = 1; i <= tot; i ++){
            addEdge(fail[i], i), addEdge(i, fail[i]);
        }
        dfs(0, 0);
        for(int i = 1; i <= n; i ++){
            cin >> x >> y;
            Q[idx[y]].emplace_back(idx[x], i);
        }
        match(0);
        for(int i = 1; i <= n; i ++){
            cout << ans[i] << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    kettle 连接 SQL Server 异常
    Quartz.NET
    过滤器和拦截器
    Spring Web MVC(一)
    Spring(三)之自动装配、表达式
    spring(四)之基于注解(Annotation-based)的配置.md
    Spring(一)之IOC、bean、注入
    Spring(二)之配置.md
    cookie和session详解
    jsp基础与提高(EL、JSTL)
  • 原文地址:https://www.cnblogs.com/onionQAQ/p/11383890.html
Copyright © 2020-2023  润新知