zoukankan      html  css  js  c++  java
  • 踹树(Trie 字典树)

    Trie 字典树

    ~~ 比 KMP 简单多了,无脑子选手学不会KMP,不会结论题~~
    自己懒得造图了OI WIKI 真棒
    字典树大概长这么个亚子

    呕吼真棒

    1. 就是将读进去的字符串根据当前的字符是什么和所处的位置构成一棵树
    2. 例如图中(1-->2-->5)这一条路就是字符串(aa)(1-->4-->8-->13)就是字符串(cab)
      貌似也没有什么东西的,题目的话基本就是套板子,唯一奇怪的就是把一个数二级化建树跑一些奇奇怪怪的东西

    字符串:

    struct Trie{
        int trie[][] ,Trie_num;
        void add(char *s){
            int now = 0 , 
            for(int i = 1 ; i <= len ;i++) {
                if(!trie[now][s[i]]) trie[now][s[i]] = +++Trie_num;
                now = trie[now][s[i]];
            }
        }
    }
    

    二进制:

    struct Trie{
        int tire[][] ,Tire_num;
        void add(int x){
            int now = 0 , 
            for(int i = 31 ; i >= 0 ;i--) {
                int ch = (x>>(i)&1);
                if(!tire[now][ch]) tire[now][ch] = +++Trie
                now = tire[now][ch];
            }
        }
    }
    

    踹树的时间复杂度其实不低普遍为(sumlimits_{i = 1}^{n}~|s_i|)

    例题
    题目洛谷也有
    这个题就是很显然的板子

    • 询问输入字符串是否为已经输入串的子串
    • 直接边建树边查询就OK

    建树并查询的时候会有2种情况

    1. 一直没有添加新的字母一直到该字符的最后一个,说明该字符串为已加入串的子串
    2. 遍历到了某个字符串的最后一个字符,说明已经添加子串为该字符串的子串

    然后就没有然后了,你就把它切了(多测不清我是sb)

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <string>
    #define ll long long
    using namespace std;
    const int N = 1e6+100;
    int read() {
        int s = 0 , f = 0 ; char ch = getchar() ;
        while(!isdigit(ch)) f |= (ch == '-') , ch = getchar();
        while(isdigit(ch)) s = s * 10 + (ch ^ 48) , ch = getchar();
        return f ? -s : s;
    }
    char s[20];
    struct Trie{
        int Tir_num ,tir[N][11],vis[N];
        int add(char *s) {
            int len = strlen(s+1), now = 0;
            bool flag = 0;
            for(int i = 1 ; i <= len ;i++) {
                int ch = s[i]-'0';
                if(!tir[now][ch]) tir[now][ch] = ++Tir_num;
                else if(i == len) flag = 1;
                // cout << Tir_num<<" ";
                now = tir[now][ch];
                if(vis[now]) flag = 1;
                // cout <<now<<" "<< flag <<" ";
            }
            vis[now] = 1;
            // puts("");
            return flag;
        }
        void clear(){
            memset(tir,0,sizeof(tir));
            memset(vis,0,sizeof(vis));
            Tir_num = 0;
            // puts("LKP AK IOI");
        }
    }tire;
    int main() {
        int T = read() ;
        while(T--) {
            tire.clear();
            int n = read();
            int cnt = 0;
            for(int i = 1 ; i <= n;i++){
                scanf("%s", s+1);
                cnt += tire.add(s);
            }
           if(cnt) puts("NO");
           else puts("YES");
        }
        system("pause");
        return 0;
    }
    

    二进制的例题

    • 根据(xor)的性质考虑贪心
    • (1)^(1) (=0~~) (0) ^ (1 = 1) (~~~~0) ^ (0 = 1)
    • 尽可能的让二级制下的高位存在1
      那么按照这个贪心思路
    void query(int x) {
        int now = 0 , ans = 0;
        for(int i = 31 ; i >= 0 ;i-- ) {
            int ch = ((x >> i) & 1) , temp = ch ^ 1;
            if(tir[now][temp]) 
                now = tir[now][temp] ,
                ans = ((ans << 1) | 1);
            else {
                now = tir[now][ch];
                ans <<= 1;
            }
        }
        return ans;
    }
    

    还是贴一下完整代码吧

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <string>
    #define ll long long
    using namespace std;
    const int N = 4000010;
    int read() {
        int s = 0 , f = 0 ; char ch = getchar() ;
        while(!isdigit(ch)) f |= (ch == '-') , ch = getchar();
        while(isdigit(ch)) s = s * 10 + (ch ^ 48) , ch = getchar();
        return f ? -s : s;
    }
    struct Trie{
        int  tire[N][2], Tir_num;
        void add(int x) {
            int now = 0 ;
            for(int i = 31 ; i >= 0 ;i--) {
                int ch = ((x>>i) & 1);
                if(!tire[now][ch]) tire[now][ch] = ++Tir_num;
                now = tire[now][ch];
            }
        }
        int query(int x){
            int now = 0 , ans = 0;
            for(int i = 31 ; i >= 0 ;i--) {
                int ch = ( (x >> i) & 1) , temp = ch ^ 1;
                if(tire[now][temp]) now = tire[now][temp],ans = (ans << 1) | 1;
                else now = tire[now][ch], ans <<= 1;
            }
            return ans;
        }
    }tir;
    
    int a[N];
    int main() {
        int n = read();
        int ans = -1;
        for(int i = 1 ; i <= n ;i++) {
            a[i] = read(); 
            tir.add(a[i]);
            ans = max(ans,tir.query(a[i]));
        }
        printf("%d",ans);
        system("pause");
        return 0;
    }
    

    踹树能做的AC自动机也能干,好吧Tire树AC自动机的一部分
    自己说的仅仅是一部分,OI wiki讲的很详细,对题目分类讲的也很详细,觉得我写的不好可以去OI wiki

  • 相关阅读:
    五、批量插入
    四、操作BLOB类型字段
    三、使用PreparedStatement实现CRUD操作
    二、获取数据库连接
    一、JDBC概述
    最短平均码长(挑出假硬币问题的解法)
    信息量和信息熵
    洛谷P2114
    Servlet续(HttpServletRequest类和HttpServletResponse类)
    Servlet
  • 原文地址:https://www.cnblogs.com/-wzd233/p/14256520.html
Copyright © 2011-2022 走看看