码迷,mamicode.com
首页 > 其他好文 > 详细

【数据结构】前缀树/字典树/Trie

时间:2018-01-28 22:01:39      阅读:277      评论:0      收藏:0      [点我收藏+]

标签:bsp   int   gpo   src   insert   快速   ring   数据结构   过程   

【前缀树】

  用来保存一个映射(通常情况下 key 为字符串  value 为字符串所代表的信息)

  例如:一个单词集合 words = {  apple, cat,  water  }   其中 key 为单词      value 代表该单词是否存在

      words[ ‘apple‘ ] = 存在     而     word[ ‘ abc‘ ] = 不存在

  图示:一个保存了8个键的trie结构,"A", "to", "tea", "ted", "ten", "i", "in", and "inn".

      键不需要被显式地保存在节点中。图示中标注出完整的单词,只是为了演示trie的原理。

  技术分享图片

          

【应用】

  trie树常用于搜索提示。如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。

  补充:

Trie是一种非常简单高效的数据结构,但有大量的应用实例。

(1) 字符串检索

事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。

举例:

@  给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

@  给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。

(2)字符串最长公共前缀

Trie树利用多个字符串的公共前缀来节省存储空间,反之,当我们把大量字符串存储到一棵trie树上时,我们可以快速得到某些字符串的公共前缀。

举例:

@ 给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少?

解决方案:首先对所有的串建立其对应的字母树。此时发现,对于两个串的最长公共前缀的长度即它们所在结点的公共祖先个数,于是,问题就转化为了离线(Offline)的最近公共祖先(Least Common Ancestor,简称LCA)问题。

而最近公共祖先问题同样是一个经典问题,可以用下面几种方法:

1. 利用并查集(Disjoint Set),可以采用采用经典的Tarjan 算法;

2. 求出字母树的欧拉序列(Euler Sequence )后,就可以转为经典的最小值查询(Range Minimum Query,简称RMQ)问题了;

(关于并查集,Tarjan算法,RMQ问题,网上有很多资料。)

(3)排序

Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。

举例:

@ 给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出。

(4) 作为其他数据结构和算法的辅助结构

  如后缀树,AC自动机等

【模板】

  该模板参考白书P209,用数组存储字典树

/*
字典树模板(注意数组初始化问题)
by chsobin 
给定n个单词的集合,询问q次:一个单词是否在集合中 
此处附加信息为节点存不存在 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxnode = 100;    //maxnode = 单词长度*单词数 

int ch[maxnode][27];     
int val[maxnode];        //附加信息 
int sz = 1;                //节点总数 

//插入字符串s,附加信息为v。注意v必须非零,0代表本节点不是单词节点 
void insert(char * s, int v){
    int u=0;
    int n = strlen(s);
    int c;
    for(int i=0;i<n;++i){
        c = s[i] - a;
        if(!ch[u][c]){ 
            ch[u][c] = sz++;    //新建节点 
        }
        u = ch[u][c];
    } 
    val[u] = v;  //1表示该节点为单词 
} 

bool find(char * s){
    int u=0;
    int n = strlen(s);
    int c;
    for(int i=0;i<n;++i){
        c = s[i] - a;
        if(ch[u][c]==0) return false;    //单词不存在
        u = ch[u][c];
    }
    if(val[u]) return true;    //判断是否为单词节点 
    else return false;
} 




int main(){
    int n, q;
    char str[30];
    scanf("%d%d", &n, &q);
    while(n--){
        scanf("%s", str);
        insert(str, 1);        //附加信息为1表示该节点为单词节点 
    } 
    while(q--){
        scanf("%s", str);
        if(find(str)) printf("yes\n");
        else printf("No\n");
    }
    return 0;
}

【例题】

  hdu1251

技术分享图片
/*
字典树/前缀树/Trie
2018年1月28日18:42:23
by chsobin

题目大意:给出n个单词(单词长度<=10),
            q个问题,问以给定字符串开头的单词有多少 
朴素思想:遍历 O(qn) 
字典树:建树 O(10n) 查询 O(10q) 
        建树过程中维护节点额外信息val[i]表示建树过程中经过节点i的单词
*/ 
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 1000000;
struct Trie{
    int ch[maxn][27];
    int val[maxn];
    int sz;
    Trie(){
        sz = 1;
        memset(val, 0, sizeof(val));
        memset(ch[0], 0, sizeof(ch[0]));
    }

    void insert(char * s){
        int n = strlen(s);
        int u = 0;
        int c;
        for(int i=0;i<n;++i){
            c = s[i] - a;
            if(!ch[u][c]){
                memset(ch[sz], 0, sizeof(ch[sz]));
                ch[u][c] = sz++;
            }
            u = ch[u][c];
            val[u]++;
        }
    }
    
    int find(char * s){
        int n = strlen(s);
        int u = 0;
        int c;
        for(int i=0;i<n;++i){
            c = s[i] - a;
            if(!ch[u][c]){
                return 0; 
            }
            u = ch[u][c];
        } 
        return val[u];
    }
};
Trie trie;
char s[15];
int main(){
    while(gets(s),strcmp(s,"")){
        trie.insert(s);
    }
    while(gets(s)!=NULL){
        printf("%d\n", trie.find(s));
    }
    return 0;
}
ac 78ms

 

【参考】

  http://dongxicheng.org/structure/trietree/

【数据结构】前缀树/字典树/Trie

标签:bsp   int   gpo   src   insert   快速   ring   数据结构   过程   

原文地址:https://www.cnblogs.com/chsobin/p/8372311.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!