zoukankan      html  css  js  c++  java
  • 【BZOJ2434】[NOI2011]阿狸的打字机

    【BZOJ2434】[NOI2011]阿狸的打字机

    题面

    bzoj

    洛谷

    题解

    我们先想一下最暴力是怎么搞的

    把$AC$自动机建好,每一个节点,从$y$串的结尾节点往上跳它的父亲,

    和普通的$AC$自动机一样跳就好了

    然而这个可以优化一下

    我们将所有询问离线

    每个串统计一次其他串对它的贡献

    就可以有$70pts$了

    $70pts$代码

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    #include <queue> 
    using namespace std;
    const int MAX_N = 2e5 + 5; 
    char s[MAX_N]; 
    int N, tot, nd[MAX_N], ans[MAX_N], sum[MAX_N]; 
    struct Trie { int ch[26], fail, fa, End; } t[MAX_N]; 
    void build() {
        static queue<int> que; 
        for (int i = 0; i < 26; i++) if (t[0].ch[i]) que.push(t[0].ch[i]), t[t[0].ch[i]].fail = 0; 
        while (!que.empty()) { 
            int o = que.front(); que.pop(); 
            for (int i = 0; i < 26; i++) {
                if (t[o].ch[i]) t[t[o].ch[i]].fail = t[t[o].fail].ch[i], que.push(t[o].ch[i]);
                else t[o].ch[i] = t[t[o].fail].ch[i]; 
            } 
        } 
    }
    int query(int y) { 
        int res = 0;
        for (int o = nd[y]; o; o = t[o].fa)
            for (int x = o; x; x = t[x].fail) if (t[x].End) ++sum[t[x].End]; 
        return res; 
    } 
    struct Query { int x, y, id; } q[MAX_N]; 
    bool operator < (const Query l, const Query r) { return l.y < r.y; } 
    
    int main () { 
        scanf("%s", s + 1);
        for (int o = 0, i = 1, l = strlen(s + 1); i <= l; i++) { 
            if ('a' <= s[i] && s[i] <= 'z') {
                if (!t[o].ch[s[i] - 'a']) t[o].ch[s[i] - 'a'] = ++tot, t[tot].fa = o; 
                o = t[o].ch[s[i] - 'a']; 
            } 
            if (s[i] == 'B') o = t[o].fa; 
            if (s[i] == 'P') nd[++N] = o, t[o].End = N; 
        }
        build(); 
        int M; scanf("%d", &M); 
        for (int i = 1; i <= M; i++) scanf("%d", &q[i].x), scanf("%d", &q[i].y), q[i].id = i;
        sort(&q[1], &q[M + 1]); 
        for (int i = 1, j = 1; i <= M; i = j) { 
            query(q[i].y); 
            while (q[j].y == q[i].y) ans[q[j].id] = sum[q[j].x], ++j; 
            fill(&sum[1], &sum[N + 1], 0); 
        } 
        for (int i = 1; i <= M; i++) printf("%d
    ", ans[i]); 
        return 0; 
    } 
    

    然后我们想一下这个过程:

    每一个点往上跳,如果可以跳到一个点,是其他字符串的$End$节点就统计贡献

    所以对于一个点,它跳到它被匹配的模式串的$End$点的次数,就是答案

    换句话说

    对于每个模式串,就是要统计有多少个文本串能跳到它。

    这个就好解决一些了,

    发现对于每个点,它对应的$fail$只有一个

    所以我们将点$i$与$fail_i$连边,

    在新的树上统计:

    每次将串$i$的链上加一,然后将询问离线统计贡献。

    但是这样做还是只有$70pts$,

    因为还是有很多状态被重复统计,

    那么如何继续优化呢?

    接下来的做法就很巧妙了:

    我们保留原来的$trie$和根据$fail$新建的树

    在新树上预处理$dfs$序,

    然后$dfs$遍历一遍原$trie$,入栈时将这个点$+1$

    出栈时$-1$

    如果这个点有结尾的串,就对这个串统计贡献,

    因为我们$dfs$回答询问时只会有那一个串被统计

    所以我们的做法是对的。

    如有什么不理解的地方,可以参见代码

    代码

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    #include <queue>
    #include <vector> 
    using namespace std;
    const int MAX_N = 2e5 + 5; 
    char s[MAX_N]; 
    int N, tot, nd[MAX_N], ans[MAX_N]; 
    struct Trie { int ch[26], cpy[26], fail, fa, End; } t[MAX_N]; 
    void build() {
        static queue<int> que; 
        for (int i = 0; i < 26; i++) if (t[0].ch[i]) que.push(t[0].ch[i]), t[t[0].ch[i]].fail = 0; 
        while (!que.empty()) { 
            int o = que.front(); que.pop(); 
            for (int i = 0; i < 26; i++) {
                if (t[o].ch[i]) t[t[o].ch[i]].fail = t[t[o].fail].ch[i], que.push(t[o].ch[i]);
                else t[o].ch[i] = t[t[o].fail].ch[i]; 
            } 
        } 
    } 
    struct Graph { int to, next; } e[MAX_N << 1]; int fir[MAX_N], e_cnt = 0; 
    void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; } 
    void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}, fir[u] = e_cnt++; } 
    struct Query { int x, id; } ; 
    vector<Query> vec[MAX_N]; 
    int tim, L[MAX_N], R[MAX_N]; 
    void dfs(int x) {
        L[x] = ++tim; 
        for (int i = fir[x]; ~i; i = e[i].next) dfs(e[i].to); 
        R[x] = tim; 
    }
    int c[MAX_N]; 
    inline int lb(int x) { return x & -x; } 
    void add(int x, int v) { while (x <= tim) c[x] += v, x += lb(x); } 
    int sum(int x) { int res = 0; while (x > 0) res += c[x], x -= lb(x); return res; } 
    void DFS(int x) { 
        add(L[x], 1); 
        if (t[x].End) 
            for (vector<Query> :: iterator ite = vec[t[x].End].begin(); ite != vec[t[x].End].end(); ++ite) 
                ans[ite->id] = sum(R[nd[ite->x]]) - sum(L[nd[ite->x]] - 1);
        for (int i = 0; i < 26; i++) 
            if (t[x].cpy[i]) DFS(t[x].cpy[i]); 
        add(L[x], -1); 
    } 
    int main () {
        scanf("%s", s + 1);
        for (int o = 0, i = 1, l = strlen(s + 1); i <= l; i++) { 
            if ('a' <= s[i] && s[i] <= 'z') {
                if (!t[o].ch[s[i] - 'a']) t[o].ch[s[i] - 'a'] = ++tot, t[tot].fa = o; 
                o = t[o].ch[s[i] - 'a']; 
            } 
            if (s[i] == 'B') o = t[o].fa; 
            if (s[i] == 'P') nd[++N] = o, t[o].End = N; 
        } 
        for (int i = 0; i <= tot; i++) 
            for (int o = 0; o < 26; o++) t[i].cpy[o] = t[i].ch[o]; 
        build(); clearGraph(); 
        for (int i = 1; i <= tot; i++) Add_Edge(t[i].fail, i); 
        dfs(0); 
        int M; scanf("%d", &M); 
        for (int i = 1; i <= M; i++) {
            int x, y; scanf("%d%d", &x, &y); 
            vec[y].push_back((Query){x, i}); 
        } 
        DFS(0); 
        for (int i = 1; i <= M; i++) printf("%d
    ", ans[i]); 
        return 0; 
    } 
    
  • 相关阅读:
    Binary Search Tree Iterator 解答
    Invert Binary Tree 解答
    Min Stack 解答
    Trapping Raining Water 解答
    Candy 解答
    Jump Game II 解答
    Implement Hash Map Using Primitive Types
    Gas Station 解答
    Bucket Sort
    HashMap 专题
  • 原文地址:https://www.cnblogs.com/heyujun/p/10242941.html
Copyright © 2011-2022 走看看