计蒜客 26985 26986 26987 | 阿里巴巴的手机代理商 | 可持久化Trie

题目链接:
https://nanti.jisuanke.com/t/26987

题目大意

实现一个程序,支持添加字符串,删除以某串为后缀的字符串,修改以某串为后缀的字符串为另一个后缀,查询以某串为后缀的字符串的个数,查询某个版本时以某串为后缀的字符串的个数。

题解

处理后缀的问题我们翻转字符串就变成处理前缀了。显然我们需要可持久化Trie实现。本篇只是贴板子。

由于题目保证所有插入删除的操作输入的字符串总长度不超过1e6,所以我们为可持久化Trie开设这么多的空间即可,因为每操作一次,可持久化Trie就会新增以操作字符串长度为个数的节点。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = int(1e6) + 10, V = int(1e5) + 10;

void *allocate();

struct Trie
{
    Trie *child[26];
    ll cnt, word;

    Trie()
    {
        for (int i = 0; i < 26; ++i)
            child[i] = nullptr;
        cnt = word = 0;
    }

    // 我们预先开设内存池,如果使用new和delete的话是很慢的
    // 因为动态申请空间耗时比较大。
    void *operator new(size_t)
    {
        return allocate();
    }

    Trie *&ch(char ch)
    {
        return child[ch - 'a'];
    }

    // 查找表示str的节点
    Trie *find(const char *str)
    {
        if (!*str)
            return this;
        else if (ch(*str) == nullptr)
            return nullptr;
        else
            return ch(*str)->find(str + 1);
    }

    // 插入str,个数为count
    static Trie *insert(Trie *node, const char *str, ll count)
    {
        Trie *here = node == nullptr ? new Trie() : new Trie(*node);
        here->cnt += count;
        if (!*str)
            here->word += count;
        else if (here->ch(*str) == nullptr)
            here->ch(*str) = insert(nullptr, str + 1, count);
        else
            here->ch(*str) = insert(here->ch(*str), str + 1, count);
        return here;
    }

    // 查询有多少个以str为前缀的字符串
    ll query(const char *str)
    {
        Trie *node = find(str);
        return node == nullptr ? 0 : node->cnt;
    }

    // 删除str,返回值为新的Trie树和删除了多少个str。
    pair<Trie *, ll> remove(const char *str)
    {
        if (!*str)
        {
            Trie *here = new Trie(*this);
            here->cnt -= word;
            here->word = 0;
            return {here, word};
        }
        else if (ch(*str) == nullptr)
            return {nullptr, 0};
        else
        {
            auto res = ch(*str)->remove(str + 1);
            if (!res.second)
                return {nullptr, 0};
            Trie *here = new Trie(*this);
            here->ch(*str) = res.first;
            here->cnt -= res.second;
            return {here, res.second};
        }
    }

    // 删除以str为前缀的所有字符串,实际作用是删除某个节点及其子节点,
    // 并更新祖先的cnt,服务update操作。
    pair<Trie *, ll> remove_prefix(const char *str)
    {
        if (!*str)
        {
            Trie *here = new Trie();
            return {here, cnt};
        }
        else if (ch(*str) == nullptr)
            return {nullptr, 0};
        else
        {
            auto res = ch(*str)->remove_prefix(str + 1);
            Trie *here = new Trie(*this);
            here->ch(*str) = res.first;
            here->cnt -= res.second;
            return {here, res.second};
        }
    }

    // 将以from为前缀的字符串替换为以to为前缀
    // 原理是将from节点的子树提取出来转接到to节点
    // 返回新的Trie和update的状态,-1为Empty,0为Conflict,1为成功修改。
    static pair<Trie *, int> update(Trie *rt, const char *from, const char *to)
    {
        Trie *f = rt->find(from), *t = rt->find(to);
        if (f == nullptr || f->cnt == 0)
            return {nullptr, -1};
        else if (t != nullptr && t->cnt)
            return {nullptr, 0};
        else
        {
            Trie x = *f;
            // 我们转接了from节点的子树后,要更新祖先节点的cnt。
            rt = rt->remove_prefix(from).first;
            // 我们更新to节点的祖先的cnt。
            rt = insert(rt, to, x.cnt);
            // 将to节点更新为from节点的信息
            *rt->find(to) = x;
            return {rt, 1};
        }
    }
} *ver[V], pool[N * 2], *poolC;

void *allocate()
{
    return poolC++;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T, n, v;
    ll c;
    string str, op, to;
    cin >> T;
    while (T--)
    {
        poolC = pool;
        ver[0] = new Trie();
        v = 0;
        cin >> n;
        while (n--)
        {
            cin >> op >> str;
            reverse(str.begin(), str.end());
            if (op == "insert")
            {
                cin >> c;
                ++v;
                ver[v] = Trie::insert(ver[v - 1], str.c_str(), c);
            }
            else if (op == "query")
            {
                ll res = ver[v]->query(str.c_str());
                cout << res << endl;
            }
            else if (op == "delete")
            {
                auto res = ver[v]->remove(str.c_str());
                if (!res.second)
                    cout << "Empty" << endl;
                else
                    ver[++v] = res.first;
            }
            else if (op == "update")
            {
                cin >> to;
                reverse(to.begin(), to.end());
                auto res = Trie::update(ver[v], str.c_str(), to.c_str());
                if (res.second == -1)
                    cout << "Empty" << endl;
                else if (res.second == 0)
                    cout << "Conflict" << endl;
                else
                    ver[++v] = res.first;
            }
            else if (op == "vquery")
            {
                cin >> c;
                cout << ver[v - c]->query(str.c_str()) << endl;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huanghongxun/article/details/80347300
今日推荐