[COCI 2015/2016 #5] OOP 题解

前言

题目链接:洛谷

本题做法有很多,可以 ACAM,可以直接变成二维数点,可以 Trie+dfs 序差分。

本题解类似最后一种做法,但是统计信息的方式略有差别,虽然时间复杂度较劣,但是支持不可差分信息,具有一定扩展性。

题意简述

给定 \(n\) 个文本串 \(s_i\)\(q\) 次查询,每次给出一个模式串 \(t_i\),包含且仅包含一个 \(\tt*\),其能匹配 \(s_i\) 当且仅当将 \(\tt*\) 替换为一个字符串(可以为空)后和 \(s_i\) 相等。对于每次查询求出其能匹配多少个文本串。

题目分析

\(n\) 个文本串插入到一棵 Trie 中。将询问拆分为 \(\tt*\) 前后两部分,根据前半部分找到对应 Trie 中结点,问题转变为了子树中终止结点对应的文本串中,有多少文本串的后缀和询问的后半部分相等。

直接这样做是错的,考虑统计到的某个文本串前后缀有交,是不合法的,但是会被统计,例如 \(\tt ab*ba\) 不能匹配 \(\tt aba\),但会被统计。

相当于查询的时候,不能在整个文本串中找到一个后缀,而是只能从叶子到当前结点对应的字符串中,找到是否存在后缀和查询的相等。

考虑用另一棵 Trie 支持插入、查询,然后做树上启发式合并。每次继承重儿子的 Trie,并在这个 Trie 的所有叶子上新增一条边,对应当前结点到重儿子的边。

考虑分析时间复杂度。一个字符串从对应结点开始,每次当做重儿子继承是常数时间,总的时间为串长;当做轻儿子暴力插入是串长减去那个点的深度,放缩不妨当做串长算,由于当做轻儿子的次数最多对数次,因此总体是线性对数。因此,时间复杂度关于总串长线性对数。空间复杂度总串长乘字符集。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1e5 + 10, M = 3e6 + 10, C = 26;

int n, q, ans[N];
string s[N], t[N];
char _s[M];

int tr2[M][C], tot2, siz2[M];
int dst[M], dtop;
inline int G() {
    int u = dtop ? dst[dtop--] : ++tot2;
    siz2[u] = 0;
    for (int i = 0; i < C; ++i) {
        if (tr2[u][i]) {
            dst[++dtop] = tr2[u][i];
            tr2[u][i] = 0;
        }
    }
    return u;
}
inline void del(int u) {
    dst[++dtop] = u;
}

int tr[M][C], tot;
vector<int> tail[M], qry[M];
inline void ins(char s[], int id) {
    int u = 0;
    for (int i = 0; s[i]; ++i) {
        int x = s[i] - 'a';
        if (!tr[u][x]) tr[u][x] = ++tot;
        u = tr[u][x];
    }
    tail[u].emplace_back(id);
}
inline void insq(char const s[], int id) {
    int u = 0;
    for (int i = 0; s[i]; ++i) {
        int x = s[i] - 'a';
        if (!tr[u][x]) return;
        u = tr[u][x];
    }
    qry[u].emplace_back(id);
}

int siz[M], son[M];
void dfs(int u) {
    siz[u] = 1;
    son[u] = -1;
    int mxsiz = 0;
    for (int i = 0; i < C; ++i) {
        int v = tr[u][i];
        if (!v) continue;
        dfs(v);
        siz[u] += siz[v];
        if (siz[v] > mxsiz) {
            mxsiz = siz[v];
            son[u] = i;
        }
    }
}
void doins(int u, int &rt, vector<int> &leaf, int d) {
    for (int i : tail[u]) {
        const string &s = ::s[i];
        int len = s.length();
        if (!rt) rt = G();
        int cur = rt;
        ++siz2[cur];
        for (int i = len - 1; i >= d; --i) {
            int x = s[i] - 'a';
            if (!tr2[cur][x]) {
                tr2[cur][x] = G();
            }
            cur = tr2[cur][x];
            ++siz2[cur];
        }
        leaf.emplace_back(cur);
    }
}
void INS(int u, int &rt, vector<int> &leaf, int d) {
    for (int i = 0; i < C; ++i) {
        int v = tr[u][i];
        if (!v) continue;
        INS(v, rt, leaf, d);
    }
    doins(u, rt, leaf, d);
}
pair<int, vector<int>> redfs(int u, int d) {
    for (int i = 0; i < C; ++i) {
        int v = tr[u][i];
        if (!v) continue;
        if (i == son[u]) continue;
        auto [rt, leaf] = redfs(v, d + 1);
        if (rt) {
            del(rt);
        }
    }
    int rt_u = 0;
    vector<int> leaf_u;
    if (son[u] != -1) {
        int i = son[u], v = tr[u][i];
        auto [rt, leaf] = redfs(v, d + 1);
        rt_u = rt;
        for (int uu : leaf) {
            if (!tr2[uu][i]) {
                tr2[uu][i] = G();
            }
            leaf_u.emplace_back(tr2[uu][i]);
            ++siz2[tr2[uu][i]];
        }
    }
    for (int i = 0; i < C; ++i) {
        int v = tr[u][i];
        if (!v) continue;
        if (i == son[u]) continue;
        INS(v, rt_u, leaf_u, d);
    }
    doins(u, rt_u, leaf_u, d);
    if (rt_u) {
        for (int qi : qry[u]) {
            int cur = rt_u;
            bool flag = false;
            for (int i = 0; i < (int)t[qi].length(); ++i) {
                int x = t[qi][i] - 'a';
                if (!tr2[cur][x]) {
                    flag = true;
                    break;
                }
                cur = tr2[cur][x];
            }
            if (flag) {
                continue;
            }
            ans[qi] = siz2[cur];
        }
    }
    return { rt_u, leaf_u };
}

int main() {
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) {
        scanf("%s", _s);
        s[i] = string(_s);
        ins(_s, i);
    }
    for (int i = 1; i <= q; ++i) {
        scanf("%s", _s);
        int j = 0;
        while (_s[j] != '*') ++j;
        string L(_s, _s + j), R(_s + j + 1, _s + strlen(_s));
        reverse(R.begin(), R.end());
        t[i] = R;
        insq(L.c_str(), i);
    }
    dfs(0);
    redfs(0, 0);
    for (int i = 1; i <= q; ++i) {
        printf("%d\n", ans[i]);
    }
    return 0;
}
posted @ 2025-07-29 20:55  XuYueming  阅读(29)  评论(0)    收藏  举报