字典树(Trie)

学习字典树一段时间 了,个人觉得字典树比较容易掌握,但是ACM中题目变化多端,我们只有多练习,才能对字典树的应用有更深的把握。

下面讲解一下字典树。

其实掌握字典树,只需要写过一个关于字典树的程序,记住它的结构就可以了。先看看字典树的定义

struct Trie
{
     Trie *next[MAX];
     bool isword;
};

其实上面那个字典树结点的定义只是来自我做的一个题目,里面的元素视你做的题目而定,不过 Trie *next[] 这个数组就是一定的了,只不过他的大小也视你的题目而定。(可能是26个小写字母,也可能是52个大小写字母或者其他)

相信一个例子大家就都掌握了,建议一定先看看题目(自己敲完这一个题目之后,一定可以A掉其他许多关于字典树的题目了)

http://acm.hdu.edu.cn/showproblem.php?pid=1247

题目的大概意思就是说给定一些单词,要你从中找到某些单词,而这个单词是由另外两个单词组成的。

其实我们就是利用字符的ascii码来给他对应的索引

比如说建树的时候存储apple这个单词,对应如下图

这个题目就是把每个单词拆成两部分,看看是不是每一部分在字典树中都是一个单词

先来看看建树的过程。(耐心一点,一步一步看懂程序,just one time)

必要的时候动手画一画图

void createTrie(char str[])//传进来输入的字符串
{
    int len = strlen(str);
    Trie *p = root,*q = NULL;
    for(int i=0;i<len;i++)
    {
         int id = str[i]-'a';
         if(p->next[id]==NULL)
         {
              q = new Trie;
              for(int j=0;j<MAX;j++)
               q->next[j] = NULL;
               q->isword = false;
              p->next[id] = q;
         }
          if(i==len-1)
          p->next[id]->isword = true;
          p = p->next[id];
    }

}


再看看查找的过程

bool findTrie(char str[])
{
     int len = strlen(str);
     Trie *p = root;
   for(int i=0;i<len;i++)
   {
        int id = str[i]-'a';
        if(p->next[id]==NULL)
        {
              return false;
        }
         p = p->next[id];
   }
   if(p->isword)
   return true;
   else
     return false;
}


最后记得释放掉这颗字典树

void del(Trie *root)
{
   for(int i=0;i<26;i++)
   {
        if(root->next[i]!=NULL)
        {
             del(root->next[i]);
        }
   }
   delete root;
}


注意在建立树的时候,先产生一个根节点 Trie *root = new Trie; //我几乎每次都忘了先new出来一个抓狂

下面贴一下完整代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = 26;
struct Trie
{
     Trie *next[MAX];
     bool isword;
};
Trie *root = new Trie;
char word[50000][30];
void createTrie(char str[])
{
    int len = strlen(str);
    Trie *p = root,*q = NULL;
    for(int i=0;i<len;i++)
    {
         int id = str[i]-'a';
         if(p->next[id]==NULL)
         {
              q = new Trie;
              for(int j=0;j<MAX;j++)
               q->next[j] = NULL;
               q->isword = false;
              p->next[id] = q;
         }
          if(i==len-1)
          p->next[id]->isword = true;
          p = p->next[id];
    }

}
bool findTrie(char str[])
{
     int len = strlen(str);
     Trie *p = root;
   for(int i=0;i<len;i++)
   {
        int id = str[i]-'a';
        if(p->next[id]==NULL)
        {
              return false;
        }
         p = p->next[id];
   }
   if(p->isword)
   return true;
   else
     return false;
}
void del(Trie *root)
{
   for(int i=0;i<MAX;i++)
   {
        if(root->next[i]!=NULL)
        {
             del(root->next[i]);
        }
   }
   delete root;
}
int main()
{
    int num=0;
    char str1[30],str2[30];
    for(int i=0;i<MAX;i++)
    {
         root->next[i] = NULL;
    }
    root->isword = false;
    while(gets(word[num]))
    {
         createTrie(word[num]);
         num++;
    }
    for(int i=0;i<num;i++)
    {
         int len = strlen(word[i]);
         if(len==1)
          continue;
        for(int j=0;j<len;j++)//从每个单词的各部分拆开
     {
          int k;
          if(j==len-1) continue;
         for(k=0;k<=j;k++)
          {
               str1[k] = word[i][k];
          }
          str1[k]='\0';
          int k2=0;
          for(int l=k;l<len;l++)
          {
               str2[k2++]=word[i][l];
          }
          str2[k2]='\0';
          if(findTrie(str1)&&findTrie(str2))
          {
               cout<<word[i]<<endl;
               break;//居然错在这里了(可能会重复输出)
          }
     }
    }
     del(root);
    return 0;
}




  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值