字典树Trie

字典树,顾名思义,就是把字典放到树上

不不不,是用字典来建树

把树建成字典的样子?

差不多。

字典树指的是某个字符串集合对应的有根树,树的每条边上恰好对应一个字符,每个顶点代表从根到该节点的路径所对应的字符串(将所有经过的边上的字符按照顺序连接起来)。有时我们也称trie上的边为转移,节点为状态

是不是读起来很恶心

其实可以这么理解

字典树就是一棵利用了字符串的公共前缀(比如gcakioi和gcakimo的前缀就是gcaki)来减少查询时间的树。当你查询单词的时候,只要沿着转移边查找就可以了,并且对于每个单词的结尾,在字典树上都会有标记来表示这是一整个单词。

举个例子

现在有两个单词

A:GCAKIOI

B:GCAKIMO

那么这个字典树大概就是这个样子

 

非常直观对吧

就是在相同的时候合到一起,不同的时候分开,在单词结尾进行标记就好了

操作实现:

1.初始化

  一棵空的trie只包含一个根节点,该点的字符指针都指向空

int ch[N][Z]//Z为字符集大小,ch[i][j]表示节点i的j字符指向的节点
bool bo[N]//对给以i为终止字符的节点打标记

 

2.插入

  当需要插入一个字符串S的时候,我们令一个指针P指向根节点,然后依次扫描S中的每个字符c

  •   如果指针P的c字符指针指向一个存在的节点Q,则令P=Q
  •        如果指针P的c字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q,然后令P=Q
  •        当S中的字符扫描完毕时,在当前节点指针P上标记它是一个字符串的末尾

 

inline void build(string s,int id)
{
    int now=1,len=s.size();
    rep(i,0,len-1)
    {
        int c=s[i]-'a';
        if(!ch[now][c])
        {
            ch[now][c]=++cnt;
        }//如果还没有这个节点,就新创建一个节点 
        now=ch[now][c];//当前节点转移过去 
    }
    bo[now]=id;//给以节点now为终止节点的打上标记 
}

 

 

 

3.查询

  当我们要查询一个字符串S在trie中是否出现时,我们令一个指针P起初指向根节点,然后依次扫描S中的每一个字符c

  •   如果指针P的c字符指针指向空,则说明S没有被插入过trie
  •        如果指针P的c字符指针指向一个已经存在的节点Q,则令P=Q
  •        当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,则说明S在trie中已经存在,否则说明S没有被插入过trie

 

inline bool find(char s[])
{
    int now=1,len=strlen(s),c,k;
    rep(i,0,len-1)//在字典树上查找该单词 
    {
        c=s[i]-'a';
        if(!ch[now][c]) return false;
        now=ch[now][c];
    }
    return true;
}

 

当字符均为小写字母或者大写字母时,trie可以看成一个26叉树

它的空间复杂度为O(N*C),N是节点个数,C是字符集的大小

posted @ 2019-06-12 15:58  小蒟蒻皮皮鱼  阅读(179)  评论(0)    收藏  举报