zoukankan      html  css  js  c++  java
  • 字典树系统学习

    学习博客:https://blog.csdn.net/SongBai1997/article/details/82317259

    这篇博客讲的挺好的,很详细,很好理解。

    Trie树(字典树)

    一、引入

    字典是干啥的?查找字的。

    字典树自然也是起查找作用的。查找的是啥?单词。

    看以下几个题:

    1、给出n个单词和m个询问,每次询问一个单词,回答这个单词是否在单词表中出现过。

    答:简单!map,短小精悍。

    好。下一个

    2、给出n个单词和m个询问,每次询问一个前缀,回答询问是多少个单词的前缀。

    答:map,把每个单词拆开。

    judge:n<=200000,TLE!

    这就需要一种高级数据结构——Trie树(字典树)

    二、原理

    在本篇文章中,假设所有单词都只由小写字母构成

    对cat,cash,app,apple,aply,ok 建一颗字典树,建成之后如下图所示

    由此可以看出:

    1、字典树用边表示字母

    2、有相同前缀的单词公用前缀节点,那我们可以的得出每个节点最多有26个子节点(在单词只包含小写字母的情况下)

    3、整棵树的根节点是空的。为什么呢?便于插入和查找,这将会在后面解释。

    4、每个单词结束的时候用一个特殊字符表示,图中用的‘′,那么从根节点到任意一个‘′,那么从根节点到任意一个‘’所经过的边的所有字母表示一个单词。

    三、基本操作

    A、insert,插入一个单词

    1.思路

      从图中可以直观看出,从左到右扫这个单词,如果字母在相应根节点下没有出现过,就插入这个字母;否则沿着字典树往下走,看单词的下一个字母。

      这就产生一个问题:往哪儿插?计算机不会自己选择位置插,我们需要给它指定一个位置,那就需要给每个字母编号。

      我们设数组trie[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点。

     什么意思呢?

     这里有2种编号,一种是i,k表示节点的位置编号,这是相对整棵树而言的;另一种是j,表示节点i的第j的孩子,这是相对节点i而言的。

     不理解?看图

     还是单词cat,cash,app,apple,aply,ok 

     我们就按输入顺序对其编第一种号,红色表示编号结果。因为先输入的cat,所以c,a,t分别是1,2,3,然后输入的是cash,因为c,a是公共前缀,所以从s开始编,s是4,以此类推。

    注意这里相同字母的编号可能不同

     

     第二种编号,相对节点的编号,紫色表示编号结果。

    因为每个节点最多有26个子节点,我们可以按他们的字典序从0——25编号,也就是他们的ASCLL码-a的ASCLL码。

    注意这里相同字母的编号相同

     实际上每个节点的子节点都应该从0编到——25,但这样会发现许多事根本用不到的。比如上图的根节点应该分出26个叉。节约空间,用到哪个分哪个。

     这样编号有什么用呢?

    回到数组trie[i][j]=k。 数组trie[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点。

    那么第二种编号即为j,第一种编号即为i,k

    2、代码

    void Insert(char s[maxn],int rt)
    {
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int x=s[i]-'a';
            if(trie[rt][x]==0)//现在插入的字母在之前同一节点未出现过
            {
                trie[rt][x]=++tot;//字母插入一个新的位置 如出现过则不用再存了
            }
            rt=trie[rt][x];//为下一个字母插入做准备
        }
        isw[rt]=true;//标记这个是单词
    }

    B、search,查找

    查找有很多种,可以查找某一个前缀,也可以查找整个单词。

    再次我们以查找一个单词是否出现过为例讲解

    1、思路

      从左往右以此扫描每个字母,顺着字典树往下找,能找到这个字母,往下走,否则结束查找,即没有这个单词;扫完了,判断是否有这个单词就行了。

    2、代码

    bool Find(char s[maxn],int rt)
    {
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int x=s[i]-'a';
            if(trie[rt][x]==0) return false;
            rt=trie[rt][x];//为查询下一个字母做准备
        }
        return isw[rt];//为啥需要这个呢?  因为你到了这里未必就有这个单词 可能只是其他单词的一个前缀罢了
    }

    完整代码:单词是否出现过:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=1e6+5;
    int trie[maxn][30];
    bool isw[maxn];
    int tot=0;
    void Insert(char s[maxn],int rt)
    {
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int x=s[i]-'a';
            if(trie[rt][x]==0)//现在插入的字母在之前同一节点未出现过
            {
                trie[rt][x]=++tot;//字母插入一个新的位置 如出现过则不用再存了
            }
            rt=trie[rt][x];//为下一个字母插入做准备
        }
        isw[rt]=true;//标记这个是单词
    }
    bool Find(char s[maxn],int rt)
    {
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int x=s[i]-'a';
            if(trie[rt][x]==0) return false;
            rt=trie[rt][x];//为查询下一个字母做准备
        }
        return isw[rt];//为啥需要这个呢?  因为你到了这里未必就有这个单词 可能只是其他单词的一个前缀罢了
    }
    int main()
    {
    
        int rt=0;
        int N;//有N个单词
        scanf("%d",&N);
        char s[maxn];
        for(int i=1;i<=N;i++)
        {
            scanf("%s",s);
            Insert(s,rt);
        }
        scanf("%d",&N);//查询N个单词是否出现过
        for(int i=1;i<=N;i++)
        {
            scanf("%s",s);
            if(Find(s,rt)) printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    (二分查找 拓展) leetcode 69. Sqrt(x)
    (二分查找 拓展) leetcode 162. Find Peak Element && lintcode 75. Find Peak Element
    (链表) lintcode 219. Insert Node in Sorted Linked List
    (二分查找 拓展) leetcode 34. Find First and Last Position of Element in Sorted Array && lintcode 61. Search for a Range
    (最短路 Floyd) P2910 [USACO08OPEN]寻宝之路Clear And Present Danger 洛谷
    (字符串 数组 递归 双指针) leetcode 344. Reverse String
    (二叉树 DFS 递归) leetcode 112. Path Sum
    (二叉树 DFS 递归) leetcode 101. Symmetric Tree
    (二叉树 递归) leetcode 144. Binary Tree Preorder Traversal
    (二叉树 递归 DFS) leetcode 100. Same Tree
  • 原文地址:https://www.cnblogs.com/caijiaming/p/11140069.html
Copyright © 2011-2022 走看看