• [一本通学习笔记] 字典树与 0-1 Trie


    字典树中根到每个结点对应原串集合的一个前缀,这个前缀由路径上所有转移边对应的字母构成。我们可以对每个结点维护一些需要的信息,这样即可以去做很多事情。

    #10049. 「一本通 2.3 例 1」Phone List

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace Trie {
        struct Node {
            Node *ch[10];
            int val;
            Node* clear() {
                for(int i=0;i<10;i++) ch[i]=NULL;
                val=0;
                return this;
            }
        };
    
        Node *root;
        Node pool[1000005];
        int ind=0;
        Node* newnode() {
            return pool[ind++].clear();
        }
    
        void clear() {
            ind=0;
            root=newnode();
        }
        void insert(string s) {
            Node *pos=root;
            for(int i=0;i<s.length();i++) {
                if(pos->ch[s[i]]==NULL) pos->ch[s[i]]=newnode();
                pos->val++;
                pos=pos->ch[s[i]];
            }
        }
        int query(string s) {
            Node *pos=root;
            for(int i=0;i<s.length();i++) {
                if(pos->ch[s[i]]==NULL) return 0;
                pos=pos->ch[s[i]];
            }
            return pos->val;
        }
    
        string str[10005];
    
        void solve() {
            int n;
            cin>>n;
            clear();
            for(int i=1;i<=n;i++) {
                cin>>str[i];
                for(int j=0;j<str[i].length();j++) str[i][j] -= '0';
                insert(str[i]);
            }
            int flag=0;
            for(int i=1;i<=n;i++) {
                if(query(str[i])) {
                    flag=1;
                    break;
                }
            }
            if(flag) cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
        }
    }
    
    int main() {
        int t;
        ios::sync_with_stdio(false);
        cin>>t;
        while(t--) Trie::solve();
        return 0;
    }

    #10050. 「一本通 2.3 例 2」The XOR Largest Pair

    0-1 Trie通常用于异或相关的问题,思路是记录所有01串后,我们找最大异或和的时候可以从高位到低位贪心,此时Trie发挥的作用就是在当前前缀已经选择的情况下,能使得正在考虑位异或为1的后缀是否存在。

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace Trie {
    struct Node {
        Node *ch[2];
        Node *clear() {
            ch[0] = ch[1] = 0;
            return this;  // Don't forget this
        }
    };
    Node *root;
    Node pool[4000005];
    int ind;
    Node *newnode() { return pool[ind++].clear(); }
    void insert(int x) {
        Node *pos = root;
        for (int i = 30; i >= 0; --i) {
            int b = (x / (1 << i)) & 1;
            if (pos->ch[b] == NULL)
                pos->ch[b] = newnode();
            pos = pos->ch[b];
        }
    }
    int query(int x) {
        Node *pos = root;
        int ans = 0;
        for (int i = 30; i >= 0 && pos != NULL; --i) {
            int b = (x / (1 << i)) & 1;
            if (pos->ch[b ^ 1] != NULL)
                pos = pos->ch[b ^ 1], ans += (1 << i);
            else
                pos = pos->ch[b];
        }
        return ans;
    }
    int a[1000005];
    void solve() {
        int n, ans = 0;
        cin >> n;
        root = newnode();  // Don't forget this
        for (int i = 1; i <= n; i++) cin >> a[i], insert(a[i]);
        for (int i = 1; i <= n; i++) ans = max(ans, query(a[i]));
        cout << ans << endl;
    }
    }  // namespace Trie
    
    int main() {
        ios::sync_with_stdio(false);
        Trie::solve();
    }

    #10051. 「一本通 2.3 例 3」Nikitosh 和异或

    看到这个算式我们很容易想到前缀和转化。问题转化为求s[r1]^s[l1-1] + s[r2]^s[l2-1]最大。

    由于要r1<l2,所以可考虑去处理出一个前缀max和一个后缀max,然后枚举分界点。那么答案就是

    Max{pre[i]+suf[i+1]}

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace Trie {
    struct Node {
        Node *ch[2];
        Node *clear() {
            ch[0] = ch[1] = 0;
            return this;  // Don't forget this
        }
    };
    Node *root;
    Node pool[8000005];
    int ind;
    Node *newnode() { return pool[ind++].clear(); }
    void insert(int x) {
        Node *pos = root;
        for (int i = 30; i >= 0; --i) {
            int b = (x / (1 << i)) & 1;
            if (pos->ch[b] == NULL)
                pos->ch[b] = newnode();
            pos = pos->ch[b];
        }
    }
    int query(int x) {
        Node *pos = root;
        int ans = 0;
        for (int i = 30; i >= 0 && pos != NULL; --i) {
            int b = (x / (1 << i)) & 1;
            if (pos->ch[b ^ 1] != NULL)
                pos = pos->ch[b ^ 1], ans += (1 << i);
            else
                pos = pos->ch[b];
        }
        return ans;
    }
    int a[1000005], pre[1000005], suf[1000005];
    void solve() {
        int n, ans = 0;
        cin >> n;
        root = newnode();  // Don't forget this
        for (int i = 1; i <= n; i++) cin >> a[i], insert(a[i]), pre[i] = max(pre[i - 1], query(a[i]));
        ind = 0;
        root = newnode();
        reverse(a + 1, a + n + 1);
        for (int i = 1; i <= n; i++) insert(a[i]), suf[i] = max(suf[i - 1], query(a[i]));
        reverse(suf + 1, suf + n + 1);
        for (int i = 1; i < n; i++) ans = max(ans, pre[i] + suf[i + 1]);
        cout << ans << endl;
    }
    }  // namespace Trie
    
    int main() {
        ios::sync_with_stdio(false);
        Trie::solve();
    }

    #10052. 「一本通 2.3 练习 1」Immediate Decodability

    和前面那题一样

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace Trie {
    struct Node {
        Node *ch[2];
        int val;
        Node *clear() {
            for (int i = 0; i < 2; i++) ch[i] = NULL;
            val = 0;
            return this;
        }
    };
    
    Node *root;
    Node pool[1000005];
    int ind = 0;
    Node *newnode() { return pool[ind++].clear(); }
    
    void clear() {
        ind = 0;
        root = newnode();
    }
    void insert(string s) {
        Node *pos = root;
        for (int i = 0; i < s.length(); i++) {
            if (pos->ch[s[i]] == NULL)
                pos->ch[s[i]] = newnode();
            pos->val++;
            pos = pos->ch[s[i]];
        }
    }
    int query(string s) {
        Node *pos = root;
        for (int i = 0; i < s.length(); i++) {
            if (pos->ch[s[i]] == NULL)
                return 0;
            pos = pos->ch[s[i]];
        }
        return pos->val;
    }
    
    string str[10005];
    
    bool solve(int t) {
        clear();
        int n = 0;
        while (cin >> str[++n]) {
            if (str[n][0] == '9')
                break;
            for (int j = 0; j < str[n].length(); j++) str[n][j] -= '0';
            insert(str[n]);
        }
        --n;
        if (n <= 0)
            return false;
        int flag = 0;
        for (int i = 1; i <= n; i++) {
            if (query(str[i])) {
                flag = 1;
                break;
            }
        }
        if (flag)
            cout << "Set " << t << " is not immediately decodable" << endl;
        else
            cout << "Set " << t << " is immediately decodable" << endl;
        return true;
    }
    }  // namespace Trie
    
    int main() {
        int t = 0;
        ios::sync_with_stdio(false);
        while (Trie::solve(++t))
            ;
        return 0;
    }

    #10053. 「一本通 2.3 练习 2」L 语言

    我们记录u[i]表示文章的每一个前缀s[1..i]是否可被理解。做一个类似dp的处理即可。

    刚开始忘记传引用T了半天……

    #include <bits/stdc++.h>
    using namespace std;
    
    char buf[1000005];
    
    void readstr(string &tar) {
        scanf("%s", buf);
        tar = buf;
    }
    
    int __cnt = 0;
    
    namespace Trie {
    struct Node {
        Node *ch[26];
        int val;
        Node *clear() {
            for (int i = 0; i < 26; i++) ch[i] = NULL;
            val = 0;
            return this;
        }
    };
    
    Node *root;
    Node pool[5000005];
    int u[1000005];
    int ind = 0, ans = 0;
    Node *newnode() { return pool[ind++].clear(); }
    
    void clear() {
        ind = 0;
        root = newnode();
    }
    void insert(string &s) {
        Node *pos = root;
        for (int i = 0; i < s.length(); i++) {
            if (pos->ch[s[i]] == NULL)
                pos->ch[s[i]] = newnode();
            pos = pos->ch[s[i]];
        }
        pos->val++;
    }
    void query(string &s, int start) {
        Node *pos = root;
        int len = s.length();
        for (int i = start; i < len; i++) {
            __cnt++;
            if (pos->ch[s[i]] == NULL)
                return;
            pos = pos->ch[s[i]];
            if (pos->val)
                u[i + 1] = 1, ans = max(ans, i + 1);
        }
    }
    
    string str[1005];
    string art;
    
    void solve() {
        int n, m;
        scanf("%d%d", &n, &m);
        clear();
        for (int i = 1; i <= n; i++) {
            readstr(str[i]);
            for (int j = 0; j < str[i].length(); j++) str[i][j] -= 'a';
            insert(str[i]);
        }
        for (int i = 1; i <= m; i++) {
            ans = 0;
            memset(u, 0, sizeof u);
            readstr(art);
            int len = art.length();
            for (int j = 0; j < len; j++) art[j] -= 'a';
            u[0] = 1;
            for (int j = 0; j < len; j++) {
                if (u[j] == 0)
                    continue;
                query(art, j);
            }
            cout << ans << endl;
        }
    }
    }  // namespace Trie
    
    int main() {
        int t;
        ios::sync_with_stdio(false);
        Trie::solve();
        // cout<<__cnt<<endl;
        return 0;
    }

    #10054. 「一本通 2.3 练习 3」Secret Message 秘密信息

    对信息建Trie,仍然是在每个串的结束点上打标记。结果就等于把密码串丢上去跑,跑的路径上的标记和,加上最终停在的结点(如果整个密码串都成功匹配)的子树的标记和。前一个直接记录,后一个用树上前缀和预处理一下即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace Trie {
    struct Node {
        Node *ch[2];
        int val, sum;
        Node *clear() {
            for (int i = 0; i < 2; i++) ch[i] = NULL;
            val = sum = 0;
            return this;
        }
    };
    
    Node *root;
    Node pool[1000005];
    int ind = 0;
    Node *newnode() { return pool[ind++].clear(); }
    
    void clear() {
        ind = 0;
        root = newnode();
    }
    void insert(int len) {
        Node *pos = root;
        for (int i = 0; i < len; i++) {
            int si;
            cin >> si;
            if (pos->ch[si] == NULL)
                pos->ch[si] = newnode();
            pos = pos->ch[si];
        }
        pos->val++;
    }
    void dfs(Node *p) {
        if (p == NULL)
            return;
        dfs(p->ch[0]);
        dfs(p->ch[1]);
        p->sum = p->val;
        if (p->ch[0])
            p->sum += p->ch[0]->sum;
        if (p->ch[1])
            p->sum += p->ch[1]->sum;
    }
    int query(int len) {
        Node *pos = root;
        int ans = 0;
        for (int i = 0; i < len; i++) {
            int si;
            cin >> si;
            ans += pos->val;
            if (pos->ch[si] == NULL) {
                for (int j = 1; j <= len - i - 1; j++) cin >> si;
                return ans;
            }
            pos = pos->ch[si];
        }
        return ans + pos->sum;
    }
    
    string str[10005];
    
    void solve() {
        int m, n;
        cin >> m >> n;
        clear();
        for (int i = 1; i <= m; i++) {
            int len;
            cin >> len;
            insert(len);
        }
        dfs(root);
        for (int i = 1; i <= n; i++) {
            int len;
            cin >> len;
            cout << query(len) << endl;
        }
    }
    }  // namespace Trie
    
    int main() {
        int t;
        ios::sync_with_stdio(false);
        Trie::solve();
        return 0;
    }

    #10056. 「一本通 2.3 练习 5」The XOR-longest Path

    树上前缀异或和以后直接转化为两点最大异或和。其实用欧拉序也可以。

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace Trie {
        struct Node {
            Node *ch[2];
            Node* clear() {
                ch[0]=ch[1]=0;
                return this; // Don't forget this
            }
        };
        Node *root;
        Node pool[4000005];
        int ind;
        Node* newnode() {
            return pool[ind++].clear();
        }
        void insert(int x) {
            Node *pos = root;
            for(int i=30;i>=0;--i) {
                int b=(x/(1<<i))&1;
                if(pos->ch[b]==NULL) pos->ch[b]=newnode();
                pos=pos->ch[b];
            }
        }
        int query(int x) {
            Node *pos = root;
            int ans = 0;
            for(int i=30;i>=0 && pos!=NULL;--i) {
                int b=(x/(1<<i))&1;
                if(pos->ch[b^1]!=NULL)
                    pos=pos->ch[b^1], ans+=(1<<i);
                else pos=pos->ch[b];
            }
            return ans;
        }
        int a[1000005];
        void solve(int n) {
            int ans=0;
            root=newnode(); // Don't forget this
            for(int i=1;i<=n;i++) insert(a[i]);
            for(int i=1;i<=n;i++) ans=max(ans,query(a[i]));
            cout<<ans<<endl;
        }
    }
    int n,vis[1000005];
    vector<pair<int,int> > g[1000005];
    
    void dfs(int p) {
        vis[p]=1;
        for(int i=0;i<g[p].size();i++) {
            int q=g[p][i].first;
            if(vis[q]==0) {
                Trie::a[q]=Trie::a[p] ^ g[p][i].second;
                dfs(q);
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin>>n;
        for(int i=1;i<n;i++) {
            int u,v,w;
            cin>>u>>v>>w;
            g[u].push_back(make_pair(v,w));
            g[v].push_back(make_pair(u,w));
        }
        dfs(1);
        Trie::solve(n);
    }
  • 相关阅读:
    Day 9
    Day 8
    Day 7
    Day 6
    Day 5
    Day 4
    Day 3
    Day 2
    Day 1
    解决vue-cli3不停请求 /sockjs-node/info?t= 问题
  • 原文地址:https://www.cnblogs.com/mollnn/p/11607187.html
Copyright © 2020-2023  润新知