[算法]字典树

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

 后缀树 suffix tree:http://www.cnblogs.com/snowberg/archive/2011/10/21/2468588.html

字典树作用:

1、查找单词是否出现

2、查找单词第一次出现位置(在最后一个字母节点标记第一次出现位置)

3、查找包含前序的单词个数(每新增一个单词进树,所经过的节点的计数器加一)

4、将所有单词按字母次序输出


举个简单的例子。


给你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级别的。所以为了节省空间。我们用动态链表,或者用数组来模拟动态。空间的花费,不会超过单词数×单词长度。

杭电AMC 1251 AC code

class node{
public:
    int count;
    node *son[26];//存放NODE的指针
    char core;
    node(char core){
        root=false;
        count=0;
        this->core=core;
        for(int i=0;i<26;++i) son[i]=NULL;
    }
    bool root;
    void add(string str,list<node> &trie);
    int getPreNum(string pre);
};

void node::add(string str, list<node> &trie){
    node *father=this;
    for(unsigned int i=0;i<str.size();++i){
        if(father->son[str[i]-'a']==NULL){
            node newson(str[i]);
            trie.push_back(newson);
            father->son[str[i]-'a']=&trie.back();
        }
        father->count++;
        father=father->son[str[i]-'a'];
    }
    father->count++;
}
int node::getPreNum(string pre){
    node *p=this;
    for(unsigned int i=0;i<pre.size();++i){
        if(p->son[pre[i]-'a']==NULL) return 0;
        p=p->son[pre[i]-'a'];
    }
    return p->count;
}

 

posted @ 2012-09-02 20:17  iyjhabc  阅读(321)  评论(0编辑  收藏  举报