P9196 题解

来一份线性时间的题解。

考虑先解决前缀限制,显然可以直接把字符串和询问全部搬到 Trie 树上,问题就变成了查询一个子树内满足后缀限制的字符串数量。

接着考虑 Trie 树合并,具体地,把后缀限制以及字符串挂在单词节点上,接着遍历整个 Trie 每到一个节点就把这个节点的儿子的所有 Trie 树合并并将这个节点挂着的字符串插入,随后回答询问,当然这个 Trie 树要从末位插入解决后缀限制。

由于每进行一次合并操作就会减少一个 Trie 上的节点又因为节点数量是 \(O(\sum |S_i| + |P_i| + |Q_i|)\) 的所以复杂度就是线性的。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 114;
struct Big_Trie {
    char c;
    int ch[4];//A G U C
} tr1[maxn];
int answer[maxn];
int tot1, rt1;
struct Small_Trie {
    char c;
    int ch[4];//A G U C
    int cnt;//单词节点数量
    int sz;
} tr2[maxn];
int tot2;
int rt2[maxn];
int merge(int a, int b) {
    if (a == 0 || b == 0)
        return a + b;

    tr2[a].ch[0] = merge(tr2[a].ch[0], tr2[b].ch[0]);
    tr2[a].ch[1] = merge(tr2[a].ch[1], tr2[b].ch[1]);
    tr2[a].ch[2] = merge(tr2[a].ch[2], tr2[b].ch[2]);
    tr2[a].ch[3] = merge(tr2[a].ch[3], tr2[b].ch[3]);
    tr2[a].cnt += tr2[b].cnt;
    tr2[a].sz = tr2[tr2[a].ch[0]].sz + tr2[tr2[a].ch[1]].sz + tr2[tr2[a].ch[2]].sz + tr2[tr2[a].ch[3]].sz +
                tr2[a].cnt;
    return a;
}
void insert(int &cur, string &s) {
    if (cur == 0)
        cur = ++tot2;

    if (s.size() == 0) {
        tr2[cur].cnt++;
        tr2[cur].sz = tr2[tr2[cur].ch[0]].sz + tr2[tr2[cur].ch[1]].sz + tr2[tr2[cur].ch[2]].sz +
                      tr2[tr2[cur].ch[3]].sz + tr2[cur].cnt;
        return ;
    } else if (s[s.size() - 1] == 'A')
        s.pop_back(), insert(tr2[cur].ch[0], s);
    else if (s[s.size() - 1] == 'G')
        s.pop_back(), insert(tr2[cur].ch[1], s);
    else if (s[s.size() - 1] == 'U')
        s.pop_back(), insert(tr2[cur].ch[2], s);
    else if (s[s.size() - 1] == 'C')
        s.pop_back(), insert(tr2[cur].ch[3], s);

    tr2[cur].sz = tr2[tr2[cur].ch[0]].sz + tr2[tr2[cur].ch[1]].sz + tr2[tr2[cur].ch[2]].sz +
                  tr2[tr2[cur].ch[3]].sz + tr2[cur].cnt;
}
int query(int cur, string &s) {
    if (cur == 0)
        return 0;
    else if (s.size() == 0) {
        return tr2[cur].sz;
    } else if (s[s.size() - 1] == 'A') {
        s.pop_back();
        return query(tr2[cur].ch[0], s);
    } else if (s[s.size() - 1] == 'G') {
        s.pop_back();
        return query(tr2[cur].ch[1], s);
    } else if (s[s.size() - 1] == 'U') {
        s.pop_back();
        return query(tr2[cur].ch[2], s);
    } else if (s[s.size() - 1] == 'C') {
        s.pop_back();
        return query(tr2[cur].ch[3], s);
    }

    return 0;
}
vector<string> str[maxn];
void Ins_str(int &cur, string &s, string &Suf) {
    if (cur == 0)
        cur = ++tot1;

    if (s.size() == 0) {
        str[cur].push_back(Suf);
        return ;
    } else if (s[s.size() - 1] == 'A')
        s.pop_back(), Ins_str(tr1[cur].ch[0], s, Suf);
    else if (s[s.size() - 1] == 'G')
        s.pop_back(), Ins_str(tr1[cur].ch[1], s, Suf);
    else if (s[s.size() - 1] == 'U')
        s.pop_back(), Ins_str(tr1[cur].ch[2], s, Suf);
    else if (s[s.size() - 1] == 'C')
        s.pop_back(), Ins_str(tr1[cur].ch[3], s, Suf);
}
vector< pair<string, int>> Q[maxn];
void Ins_ask(int &cur, string &s, string &Suf, int id) {
    if (cur == 0)
        cur = ++tot1;

    if (s.size() == 0) {
        Q[cur].push_back(make_pair(Suf, id));
        return ;
    } else if (s[s.size() - 1] == 'A')
        s.pop_back(), Ins_ask(tr1[cur].ch[0], s, Suf, id);
    else if (s[s.size() - 1] == 'G')
        s.pop_back(), Ins_ask(tr1[cur].ch[1], s, Suf, id);
    else if (s[s.size() - 1] == 'U')
        s.pop_back(), Ins_ask(tr1[cur].ch[2], s, Suf, id);
    else if (s[s.size() - 1] == 'C')
        s.pop_back(), Ins_ask(tr1[cur].ch[3], s, Suf, id);
}
void dfs(int cur) {
    if (cur == 0)
        return ;

    dfs(tr1[cur].ch[0]), rt2[cur] = merge(rt2[cur], rt2[tr1[cur].ch[0]]);
    dfs(tr1[cur].ch[1]), rt2[cur] = merge(rt2[cur], rt2[tr1[cur].ch[1]]);
    dfs(tr1[cur].ch[2]), rt2[cur] = merge(rt2[cur], rt2[tr1[cur].ch[2]]);
    dfs(tr1[cur].ch[3]), rt2[cur] = merge(rt2[cur], rt2[tr1[cur].ch[3]]);

    for (string now : str[cur]) {
        insert(rt2[cur], now);
    }

    for (pair<string, int> now : Q[cur]) {
        answer[now.second] = query(rt2[cur], now.first);
    }
}
int n, m;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;

    for (int i = 1; i <= n; i++) {
        string s, rs;
        cin >> s;

        for (int i = s.size() - 1; i >= 0; i--)
            rs.push_back(s[i]);

        Ins_str(rt1, rs, s);
    }

    for (int i = 1; i <= m; i++) {
        string p, q, rp;
        cin >> p >> q;

        for (int i = p.size() - 1; i >= 0; i--)
            rp.push_back(p[i]);

        Ins_ask(rt1, rp, q, i);
    }

    dfs(rt1);

    for (int i = 1; i <= m; i++)
        cout << answer[i] << '\n';
}
posted @ 2024-01-31 00:00  ChiFAN鸭  阅读(7)  评论(0编辑  收藏  举报