zoukankan      html  css  js  c++  java
  • 字典树trie树

    目的:已知n个长度不一定相同的母串,以及一个长度为m的模式串T,求该模式串是否是一个母串的前缀。

    时间复杂度:O(m)

    特点:牺牲空间换取时间,常用于字符串的快速检索,快速排序与去重,文本的词频统计等。

    模板

    const int MAX_N = 10000;  // Trie 树上的最大结点数
    const int MAX_C = 26;  // 每个结点的子结点个数上限
    struct Trie {
        int ch[MAX_N][MAX_C];  // ch 保存了每个结点的 26 个可能的子结点编号,26 对应着 26 种小写字母,也就是说,插入的字符串全部由小写字母组成。初始全部为 -1
        int tot;  // 总结点个数(不含根结点),初始为 0
        int cnt[MAX_N];  // 以当前结点为终端结点的 Trie 树中的字符串个数
    
        void init() {  // 初始化 Trie 树,根结点编号始终为 0
            tot = 0;
            memset(cnt, 0, sizeof(cnt));
            memset(ch, -1, sizeof(ch));
        }
    
        void insert(char *str) {
            int p = 0;  // 从根结点(0)出发
            for (int i = 0; str[i]; ++i) {
                if (ch[p][str[i] - 'a'] == -1) {  // 该子结点不存在
                    ch[p][str[i] - 'a'] = ++tot;  // 新增结点
                } 
                p = ch[p][str[i] - 'a'];  // 在 Trie 树上继续插入字符串 str
            }
            cnt[p]++;
        }
    
        int find(char *str) {  // 返回字符串 str 的出现次数
            int p = 0;
            for (int i = 0; str[i]; ++i) {
                if (ch[p][str[i] - 'a'] == -1) {
                    return 0;
                }
                p = ch[p][str[i] - 'a'];
            }
            return cnt[p];
        }
    };
    View Code

    动态分配内存模板

    const int MAX_N = 10000;  // Trie 树上的最大结点数
    const int MAX_C = 26;  // 每个结点的子结点个数上限
    struct Trie {
        int *ch[MAX_N];  // ch 保存了每个结点的 26 个可能的子结点编号,26 对应着 26 种小写字母,也就是说,插入的字符串全部由小写字母组成。初始全部为 -1
        int tot;  // 总结点个数(不含根结点),初始为 0
        int cnt[MAX_N];  // 以当前结点为终端结点的 Trie 树中的字符串个数
    
        void init() {  // 初始化 Trie 树,根结点编号始终为 0
            tot = 0;
            memset(cnt, 0, sizeof(cnt));
            memset(ch, 0, sizeof(ch));  // 将 ch 中的元素初始化为 NULL
        }
    
        void insert(char *str) {
            int p = 0;  // 从根结点(0)出发
            for (int i = 0; str[i]; ++i) {
                if (ch[p] == NULL) {
                    ch[p] = new int[MAX_C];  // 只有 p 当包含子结点的时候才去开辟 ch[p] 的空间
                    memset(ch[p], -1, sizeof(int) * MAX_C);  // 初始化为 -1
                }
                if (ch[p][str[i] - 'a'] == -1) {  // 该子结点不存在
                    ch[p][str[i] - 'a'] = ++tot;  // 新增结点
                } 
                p = ch[p][str[i] - 'a'];  // 在 Trie 树上继续插入字符串 str
            }
            cnt[p]++;
        }
    
        int find(char *str) {  // 返回字符串 str 的出现次数
            int p = 0;
            for (int i = 0; str[i]; ++i) {
                if (ch[p][str[i] - 'a'] == -1) {
                    return 0;
                }
                p = ch[p][str[i] - 'a'];
            }
            return cnt[p];
        }
    };
    View Code

    自用模板

    #include <iostream>
    #include <string.h>
    using namespace std;
    const int maxn=10010;
    struct Trie{
        int ch[maxn][26];
        int cnt[maxn];
        int num;
        void init()
        {
            memset(ch[0],0,sizeof(ch[0]));
            cnt[0]=0;
            num=0;
        }
        int newnode()
        {
            ++num;
            memset(ch[num],0,sizeof(ch[num]));
            cnt[num]=0;
            return num;
        }
        void insert(char *s)
        {
            int u=0;
            for(int i=0;s[i];i++)
            {
                if(!ch[u][s[i]-'a'])
                {
                    ch[u][s[i]-'a']=newnode();
                }
                u=ch[u][s[i]-'a'];
                ++cnt[u];
            }
        }
        int query(char *s)
        {
            int u=0;
            for(int i=0;s[i];i++)
            {
                if(!ch[u][s[i]-'a'])
                {
                    return 0;
                }
                u=ch[u][s[i]-'a'];
            }
            return cnt[u];
        }
    }trie;
    
    int main() 
    {
        trie.init();
        trie.insert("china");
        trie.insert("chinese");
        trie.insert("children");
        trie.insert("check");
        
        cout<<trie.query("")<<endl;
        cout<<trie.query("ch")<<endl;
        cout<<trie.query("chi")<<endl;
        cout<<trie.query("chin")<<endl;
        cout<<trie.query("china")<<endl;
        cout<<trie.query("beijing")<<endl;
        return 0;
    }
    View Code

    对串的快速检索 Trie树

    串的排序 Trie树+dfs

    char now[MAX_LEN];
    void dfs(int p, int len) {
        if (cnt[p] > 0) {
            now[len] = '';
            while (cnt[p] --> 0) {
                cout << now << endl;
            }
        }
        for (int i = 0; i < 26; ++i) {
            if (ch[p][i]) {
                now[len] = 'a' + i;
                dfs(ch[p][i], len + 1);
            }
        }
    }
    View Code

     n个字符串,找出最长的len,使得s1到sn中,si是si+1的前缀,且si是si+1的后缀

    exkmp处理每个字符串中哪些长度的前缀和后缀相同,trie插入时动态更新最优解,开的trie树需要动态分配内存

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e6+10;
    int dp[maxn];
    
    struct trie
    {
        int *ch[maxn];
        int cnt[maxn];
        int tot;
        void init()
        {
            memset(ch,0,sizeof ch);
            memset(cnt,0,sizeof cnt);
            tot=0;
        }
        void insert(char *s,int *next,int idx)
        {
            int p=0,n=strlen(s);
            for(int i=0;s[i];i++)
            {
                if(ch[p]==NULL)
                {
                    ch[p]=new int[26];
                    memset(ch[p],-1,sizeof(int)*26);
                }
                if(ch[p][s[i]-'A']==-1)
                {
                    ch[p][s[i]-'A']=++tot;
                }
                p=ch[p][s[i]-'A'];
                if(cnt[p])
                {
                    if(next[n-i-1]==i+1)
                    dp[idx]=max(dp[idx],dp[cnt[p]]+1);
                }
            }
            cnt[p]=idx;
        }
    }trie;
    
    void getnext(char *s,int *next)
    {
        int n=strlen(s),i,j,k;
        next[0]=n;
        for(j=0;1+j<n&&s[j]==s[1+j];j++);next[1]=j;
        k=1;
        for(i=2;i<n;i++)
        {
            int len=k+next[k],l=next[i-k];
            if(l<len-i)next[i]=l;
            else
            {
                for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
                next[i]=j;
                k=i;
            }
        }
    }
    
    
    char ch[maxn];
    int Next[maxn];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<=n;i++)dp[i]=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ch);
            getnext(ch,Next);
            trie.insert(ch,Next,i);
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            ans=max(ans,dp[i]);
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    ...

  • 相关阅读:
    Spring Boot2 系列教程(二)创建 Spring Boot 项目的三种方式
    Spring Boot2 系列教程(一)纯 Java 搭建 SSM 项目
    Python 解析XML实例(xml.sax)
    深度学习Tensorflow相关书籍推荐和PDF下载
    气象netCDF数据可视化分析
    Python 操作MySQL 数据库
    用这个库 3 分钟实现让你满意的表格功能:Bootstrap-Table
    浅谈压缩感知(二十六):压缩感知重构算法之分段弱正交匹配追踪(SWOMP)...
    web开发工具flask中文英文书籍-持续更新
    Python 在气象上的应用
  • 原文地址:https://www.cnblogs.com/myrtle/p/11470833.html
Copyright © 2011-2022 走看看