• 字典树


    Trie树就是字典树,其核心思想就是空间换时间。



    举个简单的例子。


    给你100000个长度不超过10的单词。对于每一个单词,我们要判断他出没出现过,如果出现了,第一次出现第几个位置。
    这题当然可以用hash来,但是我要介绍的是trie树。在某些方面它的用途更大。比如说对于某一个单词,我要询问它的前缀是否出现过。这样hash就不好搞了,而用trie还是很简单。
    现在回到例子中,如果我们用最傻的方法,对于每一个单词,我们都要去查找它前面的单词中是否有它。那么这个算法的复杂度就是O(n^2)。显然对于100000的范围难以接受。现在我们换个思路想。假设我要查询的单词是abcd,那么在他前面的单词中,以b,c,d,f之类开头的我显然不必考虑。而只要找以a开头的中是否存在abcd就可以了。同样的,在以a开头中的单词中,我们只要考虑以b作为第二个字母的……这样一个树的模型就渐渐清晰了……
    假设有b,abc,abd,bcd,abcd,efg,hii这6个单词,我们构建的树就是这样的。



    对于每一个节点,从根遍历到他的过程就是一个单词,如果这个节点被标记为红色,就表示这个单词存在,否则不存在。
    那么,对于一个单词,我只要顺着他从跟走到对应的节点,再看这个节点是否被标记为红色就可以知道它是否出现过了。把这个节点标记为红色,就相当于插入了这个单词。
    这样一来我们询问和插入可以一起完成,所用时间仅仅为单词长度,在这一个样例,便是10。
    我们可以看到,trie树每一层的节点数是26^i级别的。所以为了节省空间。我们用动态链表,或者用数组来模拟动态。空间的花费,不会超过单词数×单词长度。

    给出一个用类封装的字典树代码

    //HDU 1247
    #include <iostream>
    #include <algorithm>
    #include <string>
    using namespace std;
    typedef struct dictor DIC;
    DIC *root = NULL;
    struct dictor {
            dictor (){ exist = false; memset ( child , 0 , sizeof ( child ) ); }
            void insert ( char *ins );
            bool find ( const char *ins );
    private:
            DIC *child[26];
            bool exist;
    };
    void dictor::insert ( char *ins )
    {
            DIC *cur = root,*now;
            int len = strlen ( ins );
            for ( int i = 0; i < len; ++ i )
            {
                    if ( cur->child[ ins[i] - 'a' ] != NULL )
                    {
                            cur = cur->child[ ins[i] - 'a' ];
                    }
                    else
                    {
                            now = new DIC;
                            cur->child[ ins[i] - 'a' ] = now;
                            cur = now;
                    }
            }
            cur->exist = true;
    }
    bool dictor::find ( const char *ins )
    {
            DIC *cur = root;
            int len = strlen ( ins );
            for ( int i = 0; i < len; ++ i )
            {
                    if ( cur->child[ ins[i] - 'a' ] != NULL )
                            cur = cur->child[ ins[i] - 'a' ];
                    else
                            return false;
            }
            return cur->exist;
    }
    char words[50050][100];
    char s1[100],s2[100];
    DIC dict;
    int main ()
    {
        root = &dict;
        int n = 0;
        while ( scanf ( "%s",words[n] ) != EOF )
        {
                    dict.insert ( words[n++] );
        }
        for ( int i = 0; i < n; ++ i )
        {
                    int len = strlen ( words[i] );
                    for ( int j = 1; j < len; ++ j )
                    {
                            memset ( s1, 0, sizeof ( s1 ) );
                            memset ( s2, 0, sizeof ( s2 ) );
                            strncpy ( s1, words[i], j );
                            strcpy ( s2, words[i]+j );
                            if ( dict.find ( s1 ) && dict.find ( s2 ) )
                            {
                                    printf ( "%s\n", words[i] );
                                    break;
                            }
                    }
        }
        //system ( "pause" );
        return 0;
    }
    作者:BuildNewApp
    出处:http://syxchina.cnblogs.comBuildNewApp.com
    本文版权归作者、博客园和百度空间共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则作者会诅咒你的。
    如果您阅读了我的文章并觉得有价值请点击此处,谢谢您的肯定1。
  • 相关阅读:
    TIDB的体系结构学习
    TIDB操作相关命令
    TIDB的分布式事物
    FeignClient支持application/xwwwformurlencoded请求
    sublime json格式化
    VC++60MFC学习右键菜单RMenu
    VC++60新建MFC框架程序 对话框程序
    MAC 前端准备环境
    二叉树的基本知识
    电脑广告来源记录
  • 原文地址:https://www.cnblogs.com/syxchina/p/2197358.html
Copyright © 2020-2023  润新知