字典树(trie tree)

     今天AC了两题trie tree的题目,感觉trie的性质真的是相当的好,而且实现比较简单。它使在字符串集合中查找某个字符串的操作的复杂度降到最大只需O(n),其中n为字符串的长度。trie是典型的将时间置换为空间的算法,好在ACM中一般对空间的要求很宽松。

     trie的原理是利用字符串集合中字符串的公共前缀来降低时间开销以达到提高效率的目的。

它具有以下性质:1,根结点不包含任何字符信息;2,如果字符的种数为n,则每个结点的出度为n(这样必然会导致浪费很多空间,这也是trie的缺点,我还没有想到好点的办法避免);3,查找,插入复杂度为O(n),n为字符串长度。

    举一个例子,50000个由小写字母构成的长度不超过10的单词,然后问某个公共前缀是否出现过。如果我们直接从字符串集中从头往后搜,看给定的字符串是否为字符串集中某个字符串的前缀,那样复杂度为O(50000^2),这样显然会TLE。又或是我们对于字符串集中的每个字符串,我们用MAP存下它所有的前缀。然后询问时可以直接给出结果。这样复杂度为O(50000*len),最坏情况下len为字符串最长字符串的长度。而且这没有算建立MAP存储的时间,也没有算用MAP查询的时间,实际效率会更低。但如果我们用trie的话,当查询如字符串abcd是否为某字符串的前缀时,显然以b,c,d....等不是以a开头的字符串就不用查找了。实际查询复杂度只有O(len),建立trie的复杂度为O(50000).这是完全可以接受的。

    如给定字符串集合abcd,abd,cdd,efg,hij,hi六个字符串建立的trie tree如下图所示:

  

    查找一个字符串时,我们只需从根结点按字符串中字符出现顺序依次往下走。如果到最后字符串结束时,对应的结点标记为红色,则该字符串存在;否则不存在。

    插入时也只需从根结点往下遍历,碰到已存在的字符结点就往下遍历,否则,建立新结点;最后标记最后一个字符的结点为红色即可。

    同时我们看到,如果字符的种类为n,则需要结点的个数为n级数。(谁有好办法降低空间开销,请告诉我)

 

------------------------------------------------

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1251

题目和我上面举的例子差不多,是说给定一个字符串集合,然后每次询问时给出一个字符串,问以该字符串为前缀的字符串在集合中有多少个。先给个用MAP版本的,限时2000MS的题目,用MAP1750MS,险过。

 我的代码:

 1#include<iostream>
 2using namespace  std;
 3const int kind=26;
 4struct trienode{
 5    public:
 6    trienode *next[kind];    
 7    int branch;
 8    trienode()
 9    {
10     branch=0;
11     for(int i=0;i<kind;i++)
12     next[i]=NULL;    
13    }

14}
;
15class trie{
16
17    trienode *root;
18    public:
19    trie(){root=NULL;}
20    void insert(char s[])
21    {
22        trienode *location=root;
23        if(location==NULL)
24        location=root=new trienode();
25        int i=0,k;
26        while(s[i])
27        {
28            k=s[i]-'a';
29            if(location->next[k])
30                location->next[k]->branch++;
31            else
32            {
33                location->next[k]=new trienode();
34                location->next[k]->branch++;    
35            }

36            i++;
37            location=location->next[k];    
38        }

39    }

40    int search(char s[]){
41        trienode *location=root;
42        if(!location) return 0;
43        int k,i=0,ans;
44        while(s[i])
45        {
46            k=s[i]-'a';
47            if(!location->next[k])    
48             return 0;
49            ans=location->next[k]->branch;
50            location=location->next[k];
51            i++;
52        }
    
53        return ans;
54    }
    
55    
56}
;
57int main()
58{
59    char a[100];
60    int i;
61    trie mytrie;
62    while(gets(a))
63    {
64        if(a[0]=='\0')
65        break;
66        mytrie.insert(a);    
67    }

68    while(gets(a)!=NULL)
69    {
70        //if(a=="end")
71        
72        printf("%d\n",mytrie.search(a));    
73    }

74    return 0;
75}

76