zoukankan      html  css  js  c++  java
  • 数据结构 01trie树【字典树】

    trie树

    trie树是一种高效存储和查询字符串集合的数据结构

    概念:

    【算法竞赛进阶指南】:Tire(字典树)是一种用于实现字符串快速检索的多叉树结构。Trie的每个节点都拥有若干个字符指针(是trie的第二维,记录的根节点的所有儿子节点(互为兄弟节点),若在插入或检索时扫描到一个字符c,就沿着当前节点的c字符指针,走向该指针指向的节点。
    
    过程:
    
    0号节点即是根节点,又是空节点。
    
    插入:
    
      令指针p起初指向根节点,然后依次扫描字符串S中的每个字符c
    
      1.将字符c映射一下
    
      2.若当前字符c不存在,则新开辟一个节点(++ idx),令p根节点的儿子节点等于idx【也就是字符c的指针指向idx】
    
      3.让p=c的指针
    
    查询:
    
      令指针p起初指向根节点,然后依次扫描字符串S的每个字符c
    
      1.将字符c映射一下
      2.若p的c字符指向空,则说明S中没有被插入到trie树种,结束检索
    
      3.若p的c字符指针指向一个已经存在的节点Q,则令p=Q。
    
      4.当S中的字符扫描完毕时,若当前节点p被标记为一个字符串的末尾,则说明Trie中存在,否则说明S没有插入过Trie  

    Trie字符串统计

    题目:

    维护一个字符串集合,支持两种操作:
    
    I x 向集合中插入一个字符串 x;
    Q x 询问一个字符串在集合中出现了多少次。
    共有 N 个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。
    
    输入格式
    第一行包含整数 N,表示操作数。
    
    接下来 N 行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。
    
    输出格式
    对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x 在集合中出现的次数。
    
    每个结果占一行。
    
    数据范围
    1≤N≤2∗104
    输入样例:
    5
    I abc
    Q abc
    Q ab
    I ab
    Q ab
    输出样例:
    1
    0
    1 

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 100010;
    
    int n;
    char s[N], op[2];
    int tr[N][26], cnt[N], idx;
    
    void insert(char s[])
    {
        int len = strlen(s), p = 0;
        for (int i = 0; i < len; i ++ )
        {
            int t = s[i] - 'a';
            if (!tr[p][t]) tr[p][t] = ++ idx;
            p = tr[p][t];
        }
        cnt[p] ++;
    }
    
    int query(char s[])
    {
        int len = strlen(s), p = 0;
        for (int i = 0; i < len; i ++ )
        {
            int t = s[i] - 'a';
            if (!tr[p][t]) return 0;
            p = tr[p][t];
        }
        return cnt[p];
    }
    int main()
    {
        cin >> n;
        while (n -- )
        {
            scanf("%s", op);
            scanf("%s", s);
            if (*op == 'I')
            {
                insert(s);
            }
            else 
            {
                printf("%d
    ", query(s));
            }
        }
        return 0;
    }

    最大异或对

    题目:

    在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少?
    
    输入格式
    第一行输入一个整数 N。
    
    第二行输入 N 个整数 A1~AN。
    
    输出格式
    输出一个整数表示答案。
    
    数据范围
    1≤N≤105,
    0≤Ai<231
    输入样例:
    3
    1 2 3
    输出样例:
    3

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 3100010;
    
    int n;
    int tr[N][2], idx;
    //第二维记录的是根节点的儿子的兄弟节点。
    
    void insert(int x)
    {
        // cout << x << endl;
        int p = 0;
        for (int i = 31; i >= 0; i --)
        {
            int u = (x >> i & 1);
            if (!tr[p][u]) tr[p][u] = ++ idx;
            p = tr[p][u];
        }
    }
    
    int query(int x)
    {
        // cout << x << endl;
        int p = 0, ans = 0;
        for (int i = 31; i >= 0; i -- )
        {
            int u = (x >> i & 1) ^ 1;
            if (tr[p][u])
            {
                ans += 1 << i;
                p = tr[p][u];
            }
            else p = tr[p][!u];
        }
        return ans;
    }
    int main()
    {
        cin >> n;
        int res = 0;
        while (n -- )
        {
            int x; scanf("%d", &x);
            insert(x);
            res = max(res, query(x));
        }
        
        cout << res << endl;
        
        return 0;
    }

    牛异或

    题目:

    农夫约翰在给他的奶牛们喂食时遇到了一个问题。
    
    他共有 N 头奶牛,编号 1∼N。
    
    每次喂食前,这 N 头奶牛会按照 1∼N 的顺序站成一排。
    
    此外,每头奶牛都被分配了一个可能不唯一的整数。
    
    那么所有被分配的整数就形成了一个长度为 N 的整数序列。
    
    请你在该整数序列中找出一个连续的非空子序列,使得子序列中元素的异或和能够最大。
    
    如果存在多个这样的序列,那么选择序列末端整数对应的奶牛编号更小的那个序列。
    
    如果仍然存在多个可选的序列,那么选择长度最短的那个序列。
    
    输入格式
    第一行包含整数 N。
    
    第 2∼N+1 行,每行包含一个整数,其中第 i 行的整数表示编号为 i−1 的牛被分配的整数值。
    
    输出格式
    输出三个整数,分别表示最大的异或和,所选序列首端整数对应的奶牛编号,所选序列末端整数对应的奶牛编号。
    
    数据范围
    1≤N≤105,
    分配给奶牛的整数的范围是 [0,221−1]。
    
    输入样例:
    5
    1
    0
    5
    4
    2
    输出样例:
    6 4 5

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 100010;
    
    int n;
    int s[N];
    int tr[N * 21][2], id[N * 21], idx;
    
    void insert(int x, int k)
    {
        int p  = 0;
        for (int i = 21; i >= 0; i -- )
        {
            int u = x >> i & 1;
            if (!tr[p][u]) tr[p][u] = ++ idx;
            p = tr[p][u];
        }
        id[p] = k;
    }
    
    int query(int x)
    {
        int p = 0;
        for (int i = 21; i >= 0; i -- )
        {
            int u = x >> i & 1;
            if (tr[p][!u]) p = tr[p][!u];
            else p = tr[p][u];
        }
        return id[p];
    }
    int main()
    {
        cin >> n;
        for (int i = 1; i <= n ;i ++ )
        {
            scanf("%d", &s[i]);
            s[i] ^= s[i - 1];
        }
        
        int ans = -1, l = 1, r = 1;
        for (int i = 1; i <= n; i ++ )
        {
            // insert(s[i], i);
            int k = query(s[i]);
            int res = s[i] ^ s[k];
            if (ans < res) {
                ans = res;
                l = k + 1;//如果存在s[L1 ~ R] == s[L2 ~ R],且L1<L2,在insert的时候会被覆盖掉的,所以不用考虑第三个情况,会自动保留最短
                r = i;
            }
            insert(s[i], i);
        }
        
        cout << ans << ' ' << l << ' ' << r << endl;
        
        return 0;
    }
    

    最大异或和

    题目:

    给定一个非负整数数列 a,初始长度为 N。
    
    请在所有长度不超过 M 的连续子数组中,找出子数组异或和的最大值。
    
    子数组的异或和即为子数组中所有元素按位异或得到的结果。
    
    注意:子数组可以为空。
    
    输入格式
    第一行包含两个整数 N,M。
    
    第二行包含 N 个整数,其中第 i 个为 ai。
    
    输出格式
    输出可以得到的子数组异或和的最大值。
    
    数据范围
    对于 20% 的数据,1≤M≤N≤100
    对于 50% 的数据,1≤M≤N≤1000
    对于 100% 的数据,1≤M≤N≤105,0≤ai≤231−1
    输入样例:
    3 2
    1 2 4
    输出样例:
    6 

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 100010 * 31, M = 100010;
    
    int n, m;
    int s[M];
    int tr[N][2], cnt[N], idx;
    
    void insert(int x, int v)
    {
        int p = 0;
        for (int i = 31; i >= 0; i -- )
        {
            int u = x >> i & 1;
            if (!tr[p][u]) tr[p][u] = ++ idx;
            p = tr[p][u];
            cnt[p] += v;
        }
    }
    
    int query(int x)
    {
        int res = 0, p = 0;
        for (int i = 31; i >= 0; i -- )
        {
            int u = x >> i & 1;
            if (cnt[tr[p][!u]]) p =tr[p][!u], res = res * 2 + 1;
            else p = tr[p][u], res = 2 * res;
        }
        return res;
    }
    
    int main()
    {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++ )
        {
            int x; scanf("%d", &x);
            s[i] = s[i - 1] ^ x;
        }
        
        int res = 0;
        insert(s[0], 1);
        
        for (int i = 1; i <= n; i ++ )
        {
            if (i > m) insert(s[i - m - 1], -1);
            res = max(res, query(s[i]));
            insert(s[i], 1);
        }
        
        cout << res << endl;
        
        return 0;
    }
    

      

  • 相关阅读:
    OpenCascade Primitives BRep-Cone
    OpenCascade Primitives BRep-Torus
    OpenCascade Primitives BRep-Cylinder
    OpenCascade Primitives BRep
    OpenCascade Primitives BRep
    Topology and Geometry in OpenCascade-Topology
    Topology and Geometry in OpenCascade-Face
    Topology and Geometry in OpenCascade-Edge
    Topology and Geometry in OpenCascade-Vertex
    PCurve
  • 原文地址:https://www.cnblogs.com/Iamcookieandyou/p/14787413.html
Copyright © 2011-2022 走看看