Trie图的学习过程

   学习字典树有段日子了!上次去做福州赛区2010年的试题的时候,里面有一道题:字符串的多串匹配!当时就觉得应该是字典树的题,但是分析了一下后数据量太大了!就没做它!下来翻书一看,果然跟字典树有关,但不是字典树!如果用字典树肯定会超时,而不是超空间!

    不管怎么样,还是基础的数据结构和算法都没有学全,没有进行系统的学习啊!很多时候感叹,如果有老师能指点一下,如果有志同道合的朋友一起学习算法,也许结果就不是现在这个样子了!无论怎么样,坚持着往前走吧!

    字典树就很简单了,网上有很多很多的说明和代码,其思想也很容易理解:就是把字符串集合中前缀相同的部分用同一个结点表示! 回想起前两天没有研究透彻的KMP算法,以及后缀数组。发现字符串的问题,不是用到了前缀,就是用到了后缀!

    贴一张原理图,然后给出我个人觉得相当优雅的实现代码吧!

 

 

  
  
  
  
  1. /* 
  2.  http://poj.org/problem?id=3630 
  3.  POJ 3630 Phone List 
  4.  大意:电话簿中有n个电话号码,判断这些号码是否合法。 
  5.  若某个电话号码是另一个电话号码的前缀,则该号码簿非法 
  6.  分析:字典树即可 
  7.  注意点,字典树在插入过程中新建节点会超时,故节点用数组的方式存储 
  8.  */ 
  9. #include<stdio.h> 
  10. #include<string.h> 
  11. #define N 10 
  12. #define M 100000 
  13. struct TrieNode{ 
  14.     TrieNode *next[N];//由于这里面实际上已经包含了data,所以data是不需要的 
  15.     bool isTail; 
  16.     TrieNode(){ 
  17.         isTail=false
  18.         memset(next,NULL,sizeof(next)); 
  19.     } 
  20. }; 
  21. TrieNode node[M];//这个里面存储了所有的结点 
  22. class Trie{ 
  23.     public
  24.         Trie(); 
  25.         bool insert(char *word); 
  26.         void clear(); 
  27.     private
  28.         TrieNode *root; 
  29.         int nodeNum;//树中实际出现的节点数 
  30. }; 
  31. Trie::Trie(){ 
  32.     root=&node[0]; 
  33.     nodeNum = 1; 
  34. bool Trie::insert(char *word){ 
  35.     TrieNode *p=root; 
  36.  
  37.     while(*word){ 
  38.         int current=*word-'0'
  39.         if(p->next[current]==NULL){ 
  40.             p->next[current]=&node[nodeNum++]; 
  41.         } 
  42.         p=p->next[current]; 
  43.         if(p->isTail==true){ 
  44.             return true
  45.         } 
  46.         word++; 
  47.     } 
  48.     p->isTail=true
  49.     //如果它的后面还有结点,则表明此串必为一串的子串 
  50.     for(int i=0;i<N;i++){ 
  51.         if(p->next[i]!=NULL){ 
  52.             return true
  53.         } 
  54.     } 
  55.     return false
  56. void Trie::clear(){ 
  57.     for(int i=0;i<nodeNum;i++){ 
  58.          memset(node[i].next,NULL,sizeof(node[i].next)); 
  59.         node[i].isTail=false
  60.     } 
  61.     nodeNum=1;//这一步是容易忘记的 
  62. int main(){ 
  63.     int T; 
  64.     char ch[M]; 
  65.     scanf("%d",&T); 
  66.     Trie trie; 
  67.     while(T-->0){ 
  68.         int n; 
  69.         scanf("%d",&n); 
  70.         trie.clear(); 
  71.         bool isIll=false
  72.         while(n-->0){ 
  73.             scanf("%s",ch); 
  74.             if(isIll==true
  75.                 continue
  76.             isIll=trie.insert(ch); 
  77.  
  78.         } 
  79.         if(isIll){ 
  80.             printf("NO\n"); 
  81.         }else
  82.             printf("YES\n"); 
  83.         } 
  84.  
  85.     } 
  86.     return 0; 

字典树就介绍完了!可是遇到新问题的时候,比如:HDU2222,虽然题目是英文的,挺容易看懂!字典树就用不上了!那需要用什么呢?其实也有很多算法,我也没学习过,比如:AC自动机,WM算法等等!主要是KMP都没过关,那些算法也学习不了啊!但是Trie树学习过,而Trie图是可以解决这类问题的!果断学习一下Trie图吧!

    对于hdu2222,先把超时的代码贴上来吧!记得马云好像说过,多去学习失败的经验!

  
  
  
  
  1. /* 
  2.  * Trie图:解决多串匹配问题 
  3.  * hdu2222 
  4.  * */ 
  5. #include<stdio.h> 
  6. #include<string.h> 
  7. #define M 1000005 
  8. #define N 100000 
  9. char word[60]; 
  10. char s[M]; 
  11. struct Node{ 
  12.     bool tail; 
  13.     Node *next[26]; 
  14.     Node(){ 
  15.         memset(next,0,sizeof(next)); 
  16.         tail=false
  17.     } 
  18. }tree[N],*root; 
  19. int nodeNum; 
  20. void init(){ 
  21.     root=&tree[0]; 
  22.     nodeNum=1; 
  23. void insert(char *word){ 
  24.     Node *p=root; 
  25.     int idx; 
  26.     while(*word){ 
  27.         idx=*word-'a'
  28.         if(p->next[idx]==0){ 
  29.             p->next[idx]=&tree[nodeNum++]; 
  30.         } 
  31.         p=p->next[idx]; 
  32.         word++; 
  33.     } 
  34.     p->tail=true
  35. void clear(){ 
  36.     int i; 
  37.     for(i=1;i<nodeNum;i++){ 
  38.         memset(tree[i].next,0,sizeof(tree[i].next)); 
  39.         tree[i].tail=false
  40.     } 
  41.     nodeNum=1; 
  42. int search(char *s){ 
  43.     int ans=0; 
  44.     Node *p; 
  45.     int i,idx,cur,len; 
  46.     len=strlen(s); 
  47.     for(i=0;i<len;i++){ 
  48.         idx=i;p=root; 
  49.         while(1){ 
  50.             cur=s[idx]-'a'
  51.             if(p->tail==true)ans++; 
  52.             if(p->next[cur]==0)break
  53.             p=p->next[cur];idx++; 
  54.         } 
  55.     } 
  56.     return ans; 
  57. int main(){ 
  58.     int t,n,i; 
  59.     scanf("%d",&t); 
  60.     init(); 
  61.     while(t--){ 
  62.         clear(); 
  63.         scanf("%d",&n); 
  64.         for(i=0;i<n;i++){ 
  65.             scanf("%s",word); 
  66.             insert(word); 
  67.         } 
  68.         scanf("%s",s); 
  69.         printf("%d\n",search(s)); 
  70.     } 

好了,现在来看Trie图是怎么解决吧!Trie图很好懂,就是在Trie树的基础上把每个结点上增加一个前缀指针!至于Trie图的具体细节后面再学习,先把AC了的代码贴上来吧!

 

  
  
  
  
  1. /* 
  2.  * Trie图:解决多串匹配问题 
  3.  * hdu2222 
  4.  * */ 
  5. #include<stdio.h> 
  6. #include<string.h> 
  7. #include<queue> 
  8. using namespace std; 
  9. #define M 1000005 
  10. char word[60]; 
  11. char s[M]; 
  12. struct Node{ 
  13.     int tail; 
  14.     Node *prefix; 
  15.     Node *next[26]; 
  16.     Node(){ 
  17.         memset(next,0,sizeof(next)); 
  18.         tail=0; 
  19.     } 
  20. }*root; 
  21. void init(){ 
  22.     root=new Node(); 
  23. void insert(char *word){ 
  24.     Node *p=root; 
  25.     int idx; 
  26.     while(*word){ 
  27.         idx=*word-'a'
  28.         if(p->next[idx]==0){ 
  29.             p->next[idx]=new Node(); 
  30.         } 
  31.         p=p->next[idx]; 
  32.         word++; 
  33.     } 
  34.     p->tail++; 
  35. void add_prefix(){ 
  36.      root->prefix = NULL; 
  37.     deque<Node* > q; 
  38.     q.push_back(root); 
  39.  
  40.     while(!q.empty()) { 
  41.         Node* tmp = q.front(); 
  42.         Node* p = NULL; 
  43.         q.pop_front(); 
  44.         for(int i = 0; i < 26; ++i) { 
  45.             if(tmp->next[i] != NULL) { 
  46.                 if(tmp == root) tmp->next[i]->prefix = root; 
  47.                 else { 
  48.                     p = tmp->prefix; 
  49.                     while(p != NULL) { 
  50.                         if(p->next[i] != NULL) { 
  51.                             tmp->next[i]->prefix = p->next[i]; 
  52.                             break
  53.                         } 
  54.                         p = p->prefix; 
  55.                     } 
  56.                     if(p == NULL)   tmp->next[i]->prefix = root; 
  57.                 } 
  58.                 q.push_back(tmp->next[i]); 
  59.             } 
  60.         } 
  61.     } 
  62. int search(char *st){ 
  63.     int cnt = 0, t; 
  64.             Node* p = root; 
  65.             while(*st) { 
  66.                 t = *st - 'a'
  67.                 while(p->next[t] == NULL && p != root) { 
  68.                     p = p->prefix; 
  69.                 } 
  70.                 p = p->next[t]; 
  71.                 if(p == NULL)   p = root; 
  72.  
  73.                 Node* tmp = p; 
  74.                 while(tmp != root && tmp->tail != -1) { 
  75.                     cnt += tmp->tail; 
  76.                     tmp->tail = -1; 
  77.                     tmp = tmp->prefix; 
  78.                 } 
  79.                 st++; 
  80.             } 
  81.      return cnt; 
  82. int main(){ 
  83.     int t,n,i; 
  84.     scanf("%d",&t); 
  85.     while(t--){ 
  86.         init(); 
  87.         scanf("%d",&n); 
  88.         for(i=0;i<n;i++){ 
  89.             scanf("%s",word); 
  90.             insert(word); 
  91.         } 
  92.         add_prefix(); 
  93.         scanf("%s",s); 
  94.         printf("%d\n",search(s)); 
  95.     } 

 

 由于时间的关系,暂时先把Trie图的学习放到一边,把参考博客列出来,供自己以后学习吧

http://baidutech.blog.51cto.com/4114344/743727

http://www.cnblogs.com/vongang/archive/2012/07/24/2606494.html

 

 

你可能感兴趣的:(多模匹配,Trie图,HDU2222,多串匹配)