字典树 Trie

普通 Trie

模板:

namespace trie {

    template <size_t _val_max>
        struct trie {
            static constexpr size_t value_max = _val_max;// 字符集为 [0,value_max) 内的整数
            struct node {
                size_t size;
                size_t count;
                size_t father;
                array<size_t, value_max> children;
                node() : size{}, count{}, father{}, children{} {}
            };
            vector<node> tree;
            trie() : tree{} {
                tree.emplace_back();
            }
            void clear(){
                tree.clear();
                tree.emplace_back();
            }
            size_t size(){
                return tree[0].size;
            }
            size_t insert(const basic_string<size_t> &str){
                size_t nw = 0;
                ++tree[nw].size;
                for (size_t c : str){
                    if (!tree[nw].children[c])
                        tree[nw].children[c] = tree.size(), tree.emplace_back(), tree.back().father = nw;
                    nw = tree[nw].children[c]; ++tree[nw].size;
                }
                ++tree[nw].count;
                return nw;
            }
            size_t find(const basic_string<size_t> &str){
                size_t nw = 0;
                for (size_t c : str){
                    if (!tree[nw].children[c])return 0;
                    nw = tree[nw].children[c];
                }
                return nw;
            }
            size_t count(size_t id){
                return tree[id].count;
            }
            size_t count_all(size_t id){
                return tree[id].size;
            }
            void uncheck_remove(const basic_string<size_t> &str){
                size_t nw = 0;
                --tree[nw].size;
                for (size_t c : str)
                    nw = tree[nw].children[c], --tree[nw].size;
                --tree[nw].count;
            }
            size_t remove(size_t nw){
                size_t ret = tree[nw].count;
                tree[nw].count = 0;
                for (; nw; nw = tree[nw].father)tree[nw].size -= ret;
                tree[nw].size -= ret;
                return ret;
            }
            void _clear(size_t nw){
                tree[nw].size = tree[nw].count = 0;
                for (size_t i : tree[nw].children)if (i)_clear(i);
            }
            size_t remove_all(size_t nw){
                size_t ret = tree[nw].size;
                _clear(nw);
                for (; nw; nw = tree[nw].father, tree[nw].size -= ret);
                return ret;
            }
            size_t next(size_t now, size_t ch){
                return tree[now].children[ch];
            }
            template <typename _Func_t>
                void for_each(_Func_t &&func){
                    size_t nw = 0;
                    basic_string<size_t> str;
                    do {
                        bg:
                        if (tree[nw].count)func(str, tree[nw].count);
                        size_t c = 0;
                        while (1){
                            for (; c < _val_max; ++c)
                                if (tree[nw].children[c]){str.push_back(c), nw = tree[nw].children[c];goto bg;}
                            if (c == _val_max){if (!nw)return;c = str.back() + 1, str.pop_back(), nw = tree[nw].father;}
                        }
                    } while (1);
                }
        };

}//namespace trie

建立 \(n\) 个字符串 \(s_{1\sim n}\)\(\operatorname{Trie}\) 的时空复杂度都是 \(O(\sum s_i|\sum|)\)

可通过合并二度点将空间复杂度优化到 \(O(n+\sum s_i)\),称为压缩 \(\operatorname{Trie}\)

\(\operatorname{Trie}\) 的本质是一个接受且仅接受给定串的自动机

其先序遍历相当于将所有插入的字符串排序

0/1 Trie

模板:

namespace trie {

    template <typename _Tp, size_t __cnt_bit>
        struct bintrie {
            typedef _Tp value_type;
            static constexpr size_t cnt_bit = __cnt_bit;
            struct node {
                size_t size;
                array<size_t, 2> next;
                node() : size{}, next{} {}
            };
            vector<node> tree;
            bintrie() : tree{} {tree.emplace_back();}
            void clear(){tree.clear();tree.emplace_back();}
            size_t size(){return tree[0].size;}
            size_t insert(const value_type &val){
                size_t nw = 0;
                ++tree[nw].size;
                for (size_t i = cnt_bit - 1; ~i; --i){
                    size_t &rf = tree[i].next[val >> i & 1];
                    if (!rf)nw = rf = tree.size(), tree.emplace_back();
                    else nw = rf;
                    ++tree[nw].size;
                }
                return nw;
            }
            size_t find(const value_type &val){
                size_t nw = 0;
                for (size_t i = cnt_bit - 1; ~i; --i){
                    nw = tree[i].next[val >> i & 1];
                    if (!nw)return 0;
                }
                return nw;
            }
            size_t count_all(size_t id){
                return tree[id].size;
            }
            void uncheck_remove(const value_type &val){
                size_t nw = 0;
                --tree[nw].size;
                for (size_t i = cnt_bit - 1; ~i; --i)
                    --tree[nw = tree[i].next[val >> i & 1]].size;
            }
            size_t next(size_t now, bool bit){return tree[now].next[bit];}
            value_type min_xor(const value_type &a){// return min(a\oplus b | b in trie)
                if (tree[0].size == 0)return value_type{};
                value_type ret{};
                size_t nw = 0;
                for (size_t i = cnt_bit - 1; ~i; --i){
                    size_t ts = tree[i].next[a >> i & 1];
                    if (ts && tree[ts].size)nw = ts;
                    else nw = tree[i].next[(a >> i & 1) ^ 1], ret |= 1ull << i;
                }
                return ret;
            }
            value_type max_xor(const value_type &a){// return max(a\oplus b | b in trie)
                if (tree[0].size == 0)return value_type{};
                value_type ret{};
                size_t nw = 0;
                for (size_t i = cnt_bit - 1; ~i; --i){
                    size_t ts = tree[i].next[(a >> i & 1) ^ 1];
                    if (ts && tree[ts].size)nw = ts, ret |= 1ull << i;
                    else nw = tree[i].next[a >> i & 1];
                }
                return ret;
            }
            size_t count_xor_with_less(const value_type &xor_val, const value_type &compare_val){
                    //return count(xor_val \oplus b < compare_val | b in trie)
                size_t ret = 0;
                size_t nw = 0;
                for (size_t i = cnt_bit - 1; ~i; --i){
                    size_t ts = xor_val >> i & 1;
                    if (compare_val >> i & 1)// 0/1
                        ret += tree[i].next[ts]? tree[tree[i].next[0]].size : (size_t)0, nw = tree[i].next[ts ^ 1];
                    else nw = tree[i].next[ts];
                    if (!nw)return ret;
                }
                return ret;
            }
        };

}//namespace trie

维护异或和的 0/1 Trie

模板:

namespace trie {

    template <typename _Tp, size_t __cnt_bit>
        struct bintrie_xorsum {
            typedef _Tp value_type;
            static constexpr size_t cnt_bit = __cnt_bit;
            struct node {
                size_t size;
                size_t xor_sum;
                array<size_t, 2> next;
                node() : size{}, xor_sum{}, next{} {}
            };
            vector<node> tree;
            bintrie_xorsum() : tree{}{
                tree.emplace_back();
            }
            void clear(){
                tree.clear();
                tree.emplace_back();
            }
            void push_up(size_t k){
                tree[k].size = 1;
                tree[k].xor_sum = 0;
                for (size_t i : tree[k].next)
                    if (i){
                        tree[k].size += tree[i].size;
                        tree[k].xor_sum ^= tree[i].xor_sum;
                    }
            }
            size_t insert(const value_type &val){
                size_t nw = 0;
                ++tree[nw].size;
                tree[nw].xor_sum ^= val;
                for (size_t i = 0; i < cnt_bit; ++i){
                    size_t &rf = tree[i].next[val >> i & 1];
                    if (!rf)nw = rf = tree.size(), tree.emplace_back();
                    else nw = rf;
                    ++tree[nw].size; tree[nw].xor_sum ^= val;
                }
                return nw;
            }
            size_t find(const value_type &val){
                size_t nw = 0;
                for (size_t i = 0; i < cnt_bit; ++i){
                    nw = tree[i].next[val >> i & 1];
                    if (!nw)return 0;
                }
                return nw;
            }
            size_t count_all(size_t id){
                return tree[id].size;
            }
            void uncheck_remove(const value_type &val){
                size_t nw = 0;
                --tree[nw].size;
                tree[nw].xor_sum ^= val;
                for (size_t i = 0; i < cnt_bit; ++i)
                    --tree[nw = tree[i].next[val >> i & 1]].size, tree[nw].xor_sum ^= val;
            }
            void increase_all(size_t nw = 0){
                std::swap(tree[nw].next[0], tree[nw].next[1]);
                if (tree[nw].next[0])increase_all(tree[nw].next[0]);
                push_up(nw);
            }
            value_type xor_sum(size_t id = 0){
                return tree[id].xor_sum;
            }
        };

}//namespace trie

例:P6965 [NEERC2016] Binary Code

给定 \(n\)\(0/1\) 串,每串至多一位未知,可以填 \(0\)\(1\),求是否存在一种方案,使得每个字符串都不是其它任意一个字符串的前缀。字符串数量 \(\le10^5\),总长 \(\le5\times10^5\)

显然是 \(2-SAT\) 问题

但直接建边为 \(O(n^2)\) 的,因此考虑优化

在建边的过程中,\(u\rightarrow v\) 表示若选了 \(u\),则必须选 \(v\)

考虑将 \(2n\) 个字符串(\(?\)\(0\)\(1\) 时作为两个不同的字符串,分别有取/不取的状态,两者之间连边,以下不再重复)插入 \(\operatorname{Trie}\)

以下称结点为 \(\operatorname{Trie}\) 上的点,称关键点为存在字符串结尾的结点

\(x\) 表示取 \(S_x\)\(\lnot x\) 表示不取 \(S_x\)

则第一类边为 \(x\rightarrow {\lnot y}\)\(y\)\(\operatorname{Trie}\)\(x\) 的祖先或 \(x\) 的儿子)

同一个关键点可能有多个字符串结尾,因此第二类边为 若取了其中一个,则另外全部不能取,即每个字符串连向同一关键点中的其它点

可以使用前缀和优化建图和后缀和优化建图实现

时间复杂度 \(O(\sum |s|)\),常数极大

代码

参考

  1. 字符串乱学
  2. 字典树 (Trie) - oi wiki
posted @ 2025-03-15 15:08  Hstry  阅读(5)  评论(0)    收藏  举报