Trie树基本概念和训练指南

接触Trie树是在选拔赛时候遇到一题目,TLE无数次依然无解,赛后发现字符串统计有一利器名曰“字典树”,后来花了一段时间去写Trie,算是基本入门了。

本文主要是介绍一些基本概念,以及一些训练题目,提供大家。

什么叫Trie树?

Trie树即字典树。

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。

(以上来自百度百科)

在我看来,Trie树是一棵26叉树(如果是统计字母的话),典型的空间换时间。那到底我们利用Trie来做什么呢?

1.统计单词

2.匹配前缀

千篇一律地需要提到其性质:

1.根节点不包含字符,除根节点外的每一个子节点都只包含一个字符。

2.从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串

3.每个节点的所有子节点包含的字符都不相同。

而其效率主要体现,通过公共前缀尽可能减少查找复杂度

其主要操作有:插入,删除,和查询。

插入过程,其实跟普通的树一样。首先我们定义根,根不带表任何字母,对于每一个节点,固定有Size个子节点(字母就是26,数字就是10),一般用固定数组。(malloc浪费时间……)

按插入的单词查询每一个字母对应的位置(我的定义字母X对应是第X%26个位置),如果不为null,就更新子节点,查到最后一个字母时候,我们用一个terminable来记载这是结尾,当然插入过程要根据不同题目修改不同的属性值,这在做题目时候就很容易感受到。

查询的操作与插入是很相似的,我们从根开始检索依次查询单词的字母,最后返回相应的信息。

查询很多时候查询分为:有没有?(bool),有多少(int)。这样我们要随着题目改变而改变。

但有时候查询也会在插入时候操作,比如依次插入字母,问是否之前有插入的单词是现在插入的单词的前缀,那么在插入过程中,只要检索在有terminable存在,就返回true(有前缀,注意,这问题必须是前后关系情况,如果是判断插入的单词是否有互为前缀?那么我们就需要保存后(或者排序后),才能逐个查找,因为可能后出现的是前面的前缀)

下面,没拍过字典树的同学就跟着来练习吧!很快你就有感觉了。

首先,发一个模板,我是一个从开始学习语言就写C++的人,所以我在参考了别人后,用模板+类来写,喜欢c风格的同学也可以看看其他人共享的模板。(感觉有点长~但是相对我觉得好理解,假设你知道并且喜欢用template.)

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
template<int Size>
struct trie_node
{

    bool terminable;   //表示节点为字符串的结尾
    int node;           //子节点的个数
    trie_node *child[Size]; //儿子节点
    trie_node():terminable(false), node(0)
    {
        memset(child,0,sizeof(child)); //初始化节点
    }

};
template<int Size,typename Index>
class trie
{
public:
    //定义类名
    typedef trie_node<Size> node_type;
    typedef trie_node<Size> *link_type;

    //构造函数
    trie(Index i=Index()):index(i) { }

    void clear()   //清空函数,用于析构
    {
        clear_node(root);
        for(int i=0; i<Size; i++)
            root.child[i]=0;
    }
    template<typename Iterator>
    void insert(Iterator begin,Iterator end)  //插入
    {
        link_type cur= &root;//当前插入结点为根
        while(begin!=end)
        {
            if(!cur->child[index[*begin]]) //没有插入过
            {
                cur->child[index[*begin]]=new node_type;
                cur->node++;    //插入后,父亲多了一个儿子
            }
            cur=cur->child[index[*begin]]; //搜儿子
            begin++;      //迭代器往前走!
        }
        cur->terminable=true;

    }

    void insert(const char * str)  //重载c风格插入
    {
        insert(str,str+strlen(str));
    }

    template <typename Iterator>
    bool find(Iterator begin,Iterator end)    //查找
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return false;
            cur=cur->child[index[*begin]]; //搜索儿子
            begin++;
        }
        return cur->terminable; //是否为字符串
    }

    bool find(const char *str)   //重载c风格
    {
        return find(str,str+strlen(str));
    }

    template<typename Iterator>
    bool earse (Iterator begin,Iterator end)     //删除字符串
    {
        bool result;
        earse_node(begin,end,root,result);
        return result;
    }

    bool erase(char *str)  //c语言风格
    {
        return earse(str,str+strlen(str));

    }

private:

    void clear_node(node_type cur)   //清空
    {
        for(int i=0; i<Size; i++)
        {
            if(cur.child[i]==0)continue; //不存在
            clear_node(*cur.child[i]);
            delete cur.child[i];
            cur.child[i]=0;
            if(--cur.node==0) break; //没有节点了

        }

    }

    //一边搜索一边删除
    template<typename Iterator>
    bool earse_node(Iterator begin ,Iterator end,node_type &cur,bool &result)
    {
        if(begin==end)
        {
            result=cur.terminable;
            cur.terminalbe=false;
            return cur.node==0;

        }
        //当孩子不存在,结果假,返回假
        if(cur.child[index[*begin ]]==0) return !(result=false);
        else if(earse_node(begin+1,end,*(cur.child[index[*begin]]),result))
        {
            delete cur.child[index[*begin]];
            cur.child[index[*begin]]=0;
            if(--cur.node==0&&cur.terminable==false ) return true;

        }
        return false;

    }
    //根
    node_type root;
    //字符转索引,类似hash
    Index index;

};

class IndexClass
{
public:
    int operator[](const char key)
    {
        return key%26; //一个映射

    }

};

int main()
{
    trie<26,IndexClass> t;  //字母就是26,数字就是10
    t.insert("tree");       //插入
    t.insert("tea");
    t.insert("act");
    t.insert("adv");
    t.insert("ate");
    while(scanf("%s",str)!=EOF)//查找
    {
        if(t.find(str))
        {
            cout<<"find"<<endl;
        }

    }
    return 0;
}

之后,怎么少得了经典的问题呢?

HDU1251(统计难题)

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

字典树做这些问题就是神器啊。注意统计的时候,node的处理,也就是insert和find的一些改变。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
template<int Size>
struct trie_node
{

    bool terminable;   //表示节点为字符串的结尾
    int node;           //子节点的个数
    trie_node *child[Size]; //儿子节点
    trie_node():terminable(false), node(0)
    {
        memset(child,0,sizeof(child)); //初始化节点
    }

};
template<int Size,typename Index>
class trie
{
public:
    //定义类名
    typedef trie_node<Size> node_type;
    typedef trie_node<Size> *link_type;

    //构造函数
    trie(Index i=Index()):index(i) { }

    void clear()   //清空函数,用于析构
    {
        clear_node(root);
        for(int i=0; i<Size; i++)
            root.child[i]=0;
    }
    //插入
    template<typename Iterator>
    void insert(Iterator begin,Iterator end){
           link_type cur= &root;//当前插入结点为根
           while(begin!=end){
              if(cur->child[index[*begin]]){//插入过
                 cur=cur->child[index[*begin]];
                 ++(cur->node);

              }else{
                  cur->child[index[*begin]]=new node_type;    //这里这里!!!不一样!!!!
                ++(cur->child[index[*begin]]->node);
                cur=cur->child[index[*begin]];

              }

               begin++; //迭代器往前走!
           }
           cur->terminable=true;

        }

    void insert(const char * str)  //重载c风格插入
    {
        insert(str,str+strlen(str));
    }

    template <typename Iterator>
    bool find(Iterator begin,Iterator end)    //查找
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return false;
            cur=cur->child[index[*begin]]; //搜索儿子
            begin++;
        }
        return cur->terminable; //是否为字符串
    }

    bool find(const char *str)   //重载c风格
    {
        return find(str,str+strlen(str));
    }
    template <typename Iterator>
        int findNum(Iterator begin,Iterator end){
             link_type cur=&root;
             while(begin!=end){

                 if(!cur->child[index[*begin]]) //没有节点啊!!!
                     return 0;
                 cur=cur->child[index[*begin]];

                begin++;

             }

             return cur->node; //是否为字符串
        }
    //重载c风格
    int findNum(const char *str){

           return findNum(str,str+strlen(str));
    }

    template<typename Iterator>
    bool earse (Iterator begin,Iterator end)     //删除字符串
    {
        bool result;
        earse_node(begin,end,root,result);
        return result;
    }

    bool erase(char *str)  //c语言风格
    {
        return earse(str,str+strlen(str));

    }

private:

    void clear_node(node_type cur)   //清空
    {
        for(int i=0; i<Size; i++)
        {
            if(cur.child[i]==0)continue; //不存在
            clear_node(*cur.child[i]);
            delete cur.child[i];
            cur.child[i]=0;
            if(--cur.node==0) break; //没有节点了

        }

    }

    //一边搜索一边删除
    template<typename Iterator>
    bool earse_node(Iterator begin ,Iterator end,node_type &cur,bool &result)
    {
        if(begin==end)
        {
            result=cur.terminable;
            cur.terminalbe=false;
            return cur.node==0;

        }
        //当孩子不存在,结果假,返回假
        if(cur.child[index[*begin ]]==0) return !(result=false);
        else if(earse_node(begin+1,end,*(cur.child[index[*begin]]),result))
        {
            delete cur.child[index[*begin]];
            cur.child[index[*begin]]=0;
            if(--cur.node==0&&cur.terminable==false ) return true;

        }
        return false;

    }
    //根
    node_type root;
    //字符转索引,类似hash
    Index index;

};

class IndexClass
{
public:
    int operator[](const char key)
    {
        return key%26; //一个映射

    }

};

int main(){
    trie<26,IndexClass> t;
   char s[11];

    while(gets(s) && s[0])
    {
        t.insert( s);
    }

    while(gets(s))
    {
        printf("%d\n", t.findNum(s));
    }

    return 0;

}

HDU1671

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

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<vector>
using namespace std;
#define MAXN 10
template<int Size>
struct trie_node
{

    bool terminable; //表示节点为字符串的结尾
    int node;       //子节点的个数
    trie_node *child[Size]; //儿子节点
    trie_node():terminable(false), node(0)
    {
        memset(child,0,sizeof(child)); //初始化节点
    }

};
template<int Size,typename Index>
class trie
{
public:
    //定义类名
    typedef trie_node<Size> node_type;
    typedef trie_node<Size> *link_type;

    //构造函数
    trie(Index i=Index()):index(i) { }

    //清空函数,用于析构
    void clear()
    {
        clear_node(root);
        for(int i=0; i<Size; i++)
            root.child[i]=0;
    }
    //插入
    template<typename Iterator>
    void insert(Iterator begin,Iterator end)
    {
        link_type cur= &root;//当前插入结点为根
        while(begin!=end)
        {
            if(cur->child[index[*begin]]) //插入过
            {
                cur=cur->child[index[*begin]];
                cur->node++;

            }
            else
            {
                cur->child[index[*begin]]=new node_type;
                cur->child[index[*begin]]->node++;
                cur=cur->child[index[*begin]];

            }
            begin++; //迭代器往前走!
        }
        cur->terminable=true;

    }

    //重载c风格插入
    void insert(const char * str)
    {
        insert(str,str+strlen(str));
    }
    //插入
    template<typename Iterator>
    bool insert2(Iterator begin,Iterator end)
    {
        link_type cur= &root;//当前插入结点为根

         bool flag=0;
        while(begin!=end)
        {
            if(cur->child[index[*begin]]) //插入过
            {
                if(cur->child[index[*begin]]->terminable==true){

                    flag=1;
                }
                cur=cur->child[index[*begin]];
                cur->node++;

            }
            else
            {
                cur->child[index[*begin]]=new node_type;
                cur->child[index[*begin]]->node++;
                cur=cur->child[index[*begin]];

            }
            begin++; //迭代器往前走!
        }
        cur->terminable=true;
        return flag;

    }

    //重载c风格插入
    bool insert2(const char * str)
    {
        return insert2(str,str+strlen(str));
    }

    //查找
    template <typename Iterator>
    bool find(Iterator begin,Iterator end)
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return false;
            cur=cur->child[index[*begin]];

            begin++;

        }
        return cur->terminable; //是否为字符串
    }
    //重载c风格
    bool find(const char *str)
    {

        return find(str,str+strlen(str));
    }

    //查找节点数目
    template <typename Iterator>
    int findNum(Iterator begin,Iterator end)
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return 0;
            cur=cur->child[index[*begin]];

            begin++;

        }

        return cur->node; //是否为字符串
    }
    //重载c风格
    int findNum(const char *str)
    {

        return findNum(str,str+strlen(str));
    }

    //查找前缀
     template <typename Iterator>
    bool findPre(Iterator begin,Iterator end)
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return false;

            if(cur->terminable) break;
            cur=cur->child[index[*begin]];

            begin++;

        }
        return begin!=end; //是否为字符串
    }

    bool findPre(const char *str){
        return findPre(str,str+strlen(str));

    }

    //删除字符串
    template<typename Iterator>
    bool earse (Iterator begin,Iterator end)
    {
        bool result;
        earse_node(begin,end,root,result);
        return result;

    }

    //c语言风格
    bool erase(char *str)
    {
        return earse(str,str+strlen(str));

    }

    template<typename Functor>
    void traverse(Functor &execute =Functor())
    {
        visit_node(root,execute);

    }
private:
    //访问结点
    template<typename Functor>
    void visit_node(node_type cur,Functor &execute)
    {
        execute(cur);
        for(int i=0; i<Size; i++) //dfs
        {
            if(cur.child[i]==0) continue;
            visit_node(*cur.child[i],execute);

        }
    }

    //清空
    void clear_node(node_type cur)
    {
        for(int i=0; i<Size; i++)
        {
            if(cur.child[i]==0)continue; //不存在
            clear_node(*cur.child[i]);
            delete cur.child[i];
            cur.child[i]=0;
            if(--cur.node==0) break; //没有节点了

        }

    }

    //一边搜索一边删除
    template<typename Iterator>
    bool earse_node(Iterator begin ,Iterator end,node_type &cur,bool &result)
    {
        if(begin==end)
        {
            result=cur.terminable;
            cur.terminalbe=false;
            return cur.node==0;

        }
        //当孩子不存在,结果假,返回假
        if(cur.child[index[*begin ]]==0) return !(result=false);
        else if(earse_node(begin+1,end,*(cur.child[index[*begin]]),result))
        {
            delete cur.child[index[*begin]];
            cur.child[index[*begin]]=0;
            if(--cur.node==0&&cur.terminable==false ) return true;

        }
        return false;

    }
    //根
    node_type root;

    //字符转索引,类似hash
    Index index;

};

class IndexClass
{
public:
    int operator[](const char key)
    {
        return key%MAXN; //一个映射

    }

};

char s[10000][11];
int main()
{
    trie<MAXN,IndexClass> t;

    int T,n,i;
  //  freopen("in.txt","r",stdin);
    scanf("%d",&T);

    while(T--)
    {
        scanf("%d",&n);

        t.clear();
        for(i=0;i<n;i++){

            scanf("%s",s[i]);

            t.insert(s[i]);

        }
        for(i=0;i<n;i++){

            if(t.findPre(s[i])){

                puts("NO");
                break;
            }
        }
        if(i==n) puts("YES");

    }

    return 0;

}

HDU1004,或许你用map水过去了,但是你不妨试试用字典树获取0ms吧!(我的代码注释忽略吧,linux的编码跟windows有差距啊……)

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

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
template<int Size>
struct trie_node{

  bool terminable; //???????????
  int node;       //??????
  int cnt;
  trie_node *child[Size]; //????
  trie_node():terminable(false), node(0),cnt(0){
      memset(child,0,sizeof(child)); //?????
  }

};
int maxN;
char sb[30];
 char s[30];
template<int Size,typename Index>
class trie{
    public:
        //????
        typedef trie_node<Size> node_type;
        typedef trie_node<Size> *link_type;

        //????
        trie(Index i=Index()):index(i){ }

        //????,????
        void clear(){
          clear_node(root);
          for(int i=0;i<Size;i++)
              root.child[i]=0;
        }
        //??
        template<typename Iterator>
        bool insert(Iterator begin,Iterator end){
           link_type cur= &root;//????????
           while(begin!=end){
              if(!cur->child[index[*begin]]){//???

                  cur->child[index[*begin]]=new node_type;

                  cur->node++;

              }
              cur=cur->child[index[*begin]];

               begin++; //??????!
           }

           cur->terminable=true;
           cur->cnt++;
           if(cur->cnt> maxN){
              maxN=cur->cnt;
             // cout<<maxN;
             return true;
           }
           return false;

        }

        //??c????
        void insert( char * str){
            if(insert(str,str+strlen(str))){
                strcpy(sb,str);

            };
        }

    private:

       //??
       void clear_node(node_type cur){
          for(int i=0;i<Size;i++){
             if(cur.child[i]==0)continue; //???
             clear_node(*cur.child[i]);
             delete cur.child[i];
             cur.child[i]=0;
             if(--cur.node==0) break; //?????

          }

       }

       //?
       node_type root;
       //?????,??hash
       Index index;

};

class IndexClass{
    public:
        int operator[](const char key){
            return key%26; //????

        }

};

int main(){
    trie<26,IndexClass> t;

    //freopen("in.txt","r",stdin);
    int n;
    while(scanf("%d",&n)&&n)
    {

        maxN=-1;
        for(int i=0;i<n;i++){
          scanf("%s",s);

          t.insert(s);
         // cout<<maxN<<endl;
        }

        printf("%s\n",sb);
        t.clear();
    }

    return 0;

}

HDU1075

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

这题目还是很赤裸的字典树,但是我犯了一个小错,让我WA无数次。

ACcode:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
char str1[12];
char str2[12];
char st[3100];

template<int Size>
struct trie_node
{

    bool terminable; //表示节点为字符串的结尾
    int node;       //子节点的个数
    char str[12];
    trie_node *child[Size]; //儿子节点
    trie_node():terminable(false), node(0)
    {
        memset(child,0,sizeof(child)); //初始化节点
    }

};

template<int Size,typename Index>
class trie
{
public:
    //定义类名
    typedef trie_node<Size> node_type;
    typedef trie_node<Size> *link_type;

    //构造函数
    trie(Index i=Index()):index(i) { }

    //清空函数,用于析构
    void clear()
    {
        clear_node(root);
        for(int i=0; i<Size; i++)
            root.child[i]=0;
    }
    //插入
    template<typename Iterator>
    void insert(Iterator begin,Iterator end)
    {
        link_type cur= &root;//当前插入结点为根
        while(begin!=end)
        {
            if(cur->child[index[*begin]]) //插入过
            {
                cur=cur->child[index[*begin]];
                ++(cur->node);

            }
            else
            {
                cur->child[index[*begin]]=new node_type;
                ++(cur->child[index[*begin]]->node);
                cur=cur->child[index[*begin]];

            }

            begin++; //迭代器往前走!
        }
        cur->terminable=true;
        int len=strlen(str1);
        for(int i=0; i<=len; i++)
            cur->str[i]=str1[i];

    }

    //重载c风格插入
    void insert(const char * str)
    {
        insert(str,str+strlen(str));
    }

    //查找
    template <typename Iterator>
    bool find(Iterator begin,Iterator end)
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return false;
            cur=cur->child[index[*begin]];

            begin++;

        }
        //     printf("%s sb",cur->str);
        if(cur->terminable)
        {
            printf("%s",cur->str);
            return true;

        }
        return false; //是否为字符串
    }

    //重载c风格
    bool find(const char *str)
    {

        return find(str,str+strlen(str));
    }

private:

    //清空
    void clear_node(node_type cur)
    {
        for(int i=0; i<Size; i++)
        {
            if(cur.child[i]==0)continue; //不存在
            clear_node(*cur.child[i]);
            delete cur.childe[i];
            cur.child[i]=0;
            if(--cur.node==0) break; //没有节点了

        }

    }

    //根
    node_type root;
    //字符转索引,类似hash
    Index index;

};

class IndexClass
{
public:
    int operator[](const char key)
    {
        return key%26; //一个映射

    }

};

int main()
{
    trie<26,IndexClass> t;
    scanf("%s",str2) ;

    while(scanf("%s",str1))
    {
        if(str1[0]==‘E‘)
        {

            break;
        }
        scanf("%s",str2);
        t.insert(str2);
    }
    scanf("%s",str2);

    getchar();

    while(gets(st))
    {

        if(st[0]==‘E‘)
        {

            break;
        }
        int len=strlen(st);
        int bg=0;
        for(int i=0; i<len; i++)
        {
            if(st[i]>=‘a‘&&st[i]<=‘z‘)
            {

                continue;
            }
            if(!t.find(st+bg,st+i))
            {
                for(int j=bg; j<i; j++)
                  printf("%c",st[j]);

            };
            if(st[i]) printf("%c",st[i]);
            bg=i+1;

        }

        puts("");

    }

    return 0;

}

WAcode:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
char str1[12];
char str2[12];
char st[3100];

template<int Size>
struct trie_node
{

    bool terminable; //表示节点为字符串的结尾
    int node;       //子节点的个数
    char str[12];
    trie_node *child[Size]; //儿子节点
    trie_node():terminable(false), node(0)
    {
        memset(child,0,sizeof(child)); //初始化节点
    }

};

template<int Size,typename Index>
class trie
{
public:
    //定义类名
    typedef trie_node<Size> node_type;
    typedef trie_node<Size> *link_type;

    //构造函数
    trie(Index i=Index()):index(i) { }

    //清空函数,用于析构
    void clear()
    {
        clear_node(root);
        for(int i=0; i<Size; i++)
            root.child[i]=0;
    }
    //插入
    template<typename Iterator>
    void insert(Iterator begin,Iterator end)
    {
        link_type cur= &root;//当前插入结点为根
        while(begin!=end)
        {
            if(cur->child[index[*begin]]) //插入过
            {
                cur=cur->child[index[*begin]];
                ++(cur->node);

            }
            else
            {
                cur->child[index[*begin]]=new node_type;
                ++(cur->child[index[*begin]]->node);
                cur=cur->child[index[*begin]];

            }

            begin++; //迭代器往前走!
        }
        cur->terminable=true;
        int len=strlen(str1);
        for(int i=0; i<len; i++)
            cur->str[i]=str1[i];

    }

    //重载c风格插入
    void insert(const char * str)
    {
        insert(str,str+strlen(str));
    }

    //查找
    template <typename Iterator>
    bool find(Iterator begin,Iterator end)
    {
        link_type cur=&root;
        while(begin!=end)
        {

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return false;
            cur=cur->child[index[*begin]];

            begin++;

        }
        //     printf("%s sb",cur->str);
        if(cur->terminable)
        {
            printf("%s",cur->str);
            return true;

        }
        return false; //是否为字符串
    }

    //重载c风格
    bool find(const char *str)
    {

        return find(str,str+strlen(str));
    }

private:

    //清空
    void clear_node(node_type cur)
    {
        for(int i=0; i<Size; i++)
        {
            if(cur.child[i]==0)continue; //不存在
            clear_node(*cur.child[i]);
            delete cur.childe[i];
            cur.child[i]=0;
            if(--cur.node==0) break; //没有节点了

        }

    }

    //根
    node_type root;
    //字符转索引,类似hash
    Index index;

};

class IndexClass
{
public:
    int operator[](const char key)
    {
        return key%26; //一个映射

    }

};

int main()
{
    trie<26,IndexClass> t;
    scanf("%s",str2) ;

    while(scanf("%s",str1))
    {
        if(str1[0]==‘E‘)
        {

            break;
        }
        scanf("%s",str2);
        t.insert(str2);
    }
    scanf("%s",str2);

    getchar();

    while(gets(st))
    {

        if(st[0]==‘E‘)
        {

            break;
        }
        int len=strlen(st);
        int bg=0;
        for(int i=0; i<len; i++)
        {
            if(st[i]>=‘a‘&&st[i]<=‘z‘)
            {

                continue;
            }
            if(!t.find(st+bg,st+i))
            {
                for(int j=bg; j<i; j++)
                  printf("%c",st[j]);

            };
            if(st[i]) printf("%c",st[i]);
            bg=i+1;

        }

        puts("");

    }

    return 0;

}

(差别就在字符串copy 上没有拷贝上‘\0‘……,也就是st[len])

HDU1247

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

题目相对理解上会有点偏差,不是当且仅当两个,是两个,举个例子

a

abc

b

bc

c

输出的是:abc,bc(注意abc也是,不要因为abc=a+b+c,而忽略,只要可以满足s=s1+s2即可,即abc=a+bc,bc=b+c)

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
template<int Size>
struct trie_node{

  bool terminable; //表示节点为字符串的结尾
  int node;       //子节点的个数

  trie_node *child[Size]; //儿子节点
  trie_node():terminable(false), node(0){
      memset(child,0,sizeof(child)); //初始化节点
  }

};

template<int Size,typename Index>
class trie{
    public:
        //定义类名
        typedef trie_node<Size> node_type;
        typedef trie_node<Size> *link_type;

        //构造函数
        trie(Index i=Index()):index(i){ }

        //清空函数,用于析构
        void clear(){
          clear_node(root);
          for(int i=0;i<Size;i++)
              root.child[i]=0;
        }
        //插入
        template<typename Iterator>
        void insert(Iterator begin,Iterator end){

           link_type cur= &root;//当前插入结点为根
           while(begin!=end){
              if(cur->child[index[*begin]]){//插入过
                 cur=cur->child[index[*begin]];
                 ++(cur->node);

              }else{
                  cur->child[index[*begin]]=new node_type;
                ++(cur->child[index[*begin]]->node);
                cur=cur->child[index[*begin]];

              }

              // cout<<*begin;
               begin++; //迭代器往前走!
           }
           cur->terminable=true;

          // cout<<cur->id<<endl;

        }

        //重载c风格插入
        void insert(const char * str){
           insert(str,str+strlen(str));
        }

        //查找
        template <typename Iterator>
       bool find(Iterator begin,Iterator end){
             link_type cur=&root;

             while(begin!=end){

                if(!cur->child[index[*begin]])return false;  //我在这里re了无数次…忧桑……

                 cur=cur->child[index[*begin]];

                begin++;

             }
          //    cout<<*begin<<" "<<*end<<"sb" <<endl;
            return cur->terminable;

        }

        //重载c风格
          bool  find(const char *str){

           return  find(str,str+strlen(str));
        }

    private:

       //清空
       void clear_node(node_type cur){
          for(int i=0;i<Size;i++){
             if(cur.child[i]==0)continue; //不存在
             clear_node(*cur.child[i]);
             delete cur.childe[i];
             cur.child[i]=0;
             if(--cur.node==0) break; //没有节点了

          }

       }

       //根
       node_type root;
       //字符转索引,类似hash
       Index index;

};

class IndexClass{
    public:
        int operator[](const char key){
            return key%26; //一个映射

        }

};
char str[50005][100];

int main(){
    trie<26,IndexClass> t;
    int i=0;

    while(scanf("%s",str[i])!=EOF){
          //  cout<<str[i];
        t.insert(str[i]);
        i++;
    }

    for(int j=0;j<i;j++){

           int len=strlen(str[j]);
         for(int p=1;p<=len-1;p++){

          if(t.find(str[j],str[j]+p)){

              if(t.find(str[j]+p)){

                printf("%s\n",str[j]);
                break;
              }

          }

       }

    }

    return 0;

}

尽管我上面这个做法已经OK了,但是这是参考了别人,下面是我完全按自己想法写的,程序员最高兴的莫过于可以将自己的想法实现。

其实我就是用了字典树+dfs

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
template<int Size>
struct trie_node{

  bool terminable; //表示节点为字符串的结尾
  int node;       //子节点的个数

  trie_node *child[Size]; //儿子节点
  trie_node():terminable(false), node(0){
      memset(child,0,sizeof(child)); //初始化节点
  }

};

template<int Size,typename Index>
class trie{
    public:
        //定义类名
        typedef trie_node<Size> node_type;
        typedef trie_node<Size> *link_type;

        //构造函数
        trie(Index i=Index()):index(i){ }

        //清空函数,用于析构
        void clear(){
          clear_node(root);
          for(int i=0;i<Size;i++)
              root.child[i]=0;
        }
        //插入
        template<typename Iterator>
        void insert(Iterator begin,Iterator end){

           link_type cur= &root;//当前插入结点为根
           while(begin!=end){
              if(cur->child[index[*begin]]){//插入过
                 cur=cur->child[index[*begin]];
                 ++(cur->node);

              }else{
                  cur->child[index[*begin]]=new node_type;
                ++(cur->child[index[*begin]]->node);
                cur=cur->child[index[*begin]];

              }

              // cout<<*begin;
               begin++; //迭代器往前走!
           }
           cur->terminable=true;

          // cout<<cur->id<<endl;

        }

        //重载c风格插入
        void insert(const char * str){
           insert(str,str+strlen(str));
        }

        //查找
        template <typename Iterator>
       bool find(Iterator begin,Iterator end,int i){
             link_type cur=&root;

             while(begin!=end){

                if(cur->terminable){
                    if(i>1) return false;
                    if( find(begin,end,i+1))return true;
                }

                if(!cur->child[index[*begin]]) return false;
                 cur=cur->child[index[*begin]];

                begin++;

             }
             if(!cur->terminable){
                 return false;
             }
            return i==1;

        }

        //重载c风格
          bool  find(const char *str,int i){

           return  find(str,str+strlen(str),i);
        }

    private:

       //清空
       void clear_node(node_type cur){
          for(int i=0;i<Size;i++){
             if(cur.child[i]==0)continue; //不存在
             clear_node(*cur.child[i]);
             delete cur.child[i];
             cur.child[i]=0;
             if(--cur.node==0) break; //没有节点了

          }

       }

       //根
       node_type root;
       //字符转索引,类似hash
       Index index;

};

class IndexClass{
    public:
        int operator[](const char key){
            return key%26; //一个映射

        }

};
char str[50005][50];

int main(){
    trie<26,IndexClass> t;
    int i=0;
   //freopen("in.txt","r",stdin);
    while(scanf("%s",str[i])!=EOF){
          //  cout<<str[i];
        t.insert(str[i]);
        i++;
    }

    for(int j=0;j<i;j++){

        if(t.find(str[j],0)){ //类似与dfss

        printf("%s\n",str[j]);
       }

    }

    return 0;

}

HDU1857,逆向思维的trie,可以参考我之前写过的

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

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
template<int Size>
struct trie_node{

    bool terminable; //表示节点为字符串的结尾
    int node; //子节点的个数
    int id;
    trie_node *child[Size]; //儿子节点
    trie_node():terminable(false), node(0){
        memset(child,0,sizeof(child)); //初始化节点
    }

};
int RR[10200],CC[10200];
template<int Size,typename Index>
class trie{
public:
    //定义类名
    typedef trie_node<Size> node_type;
    typedef trie_node<Size> *link_type;

    //构造函数
    trie(Index i=Index()):index(i){ }

    //清空函数,用于析构
    void clear(){
        clear_node(root);
        for(int i=0;i<Size;i++)
            root.child[i]=0;
    }
    //插入
    template<typename Iterator>
        void insert(Iterator begin,Iterator end,int i){

        link_type cur= &root;//当前插入结点为根
        while(begin!=end){
            if(cur->child[index[*begin]]){//插入过
                cur=cur->child[index[*begin]];
                ++(cur->node);

            }else{
                cur->child[index[*begin]]=new node_type;
                ++(cur->child[index[*begin]]->node);
                cur=cur->child[index[*begin]];

            }

            begin++; //迭代器往前走!
        }
        cur->terminable=true;
        cur->id=i;

    }

    //重载c风格插入
    void insert(const char * str,int i){
        insert(str,str+strlen(str), i);
    }

    //查找
    template <typename Iterator>
        void find(Iterator begin,Iterator end,int r,int c){
        link_type cur=&root;
        while(begin!=end){

            if(cur->terminable){

                if(RR[cur->id]==0){

                    RR[cur->id]=r;
                    CC[cur->id]=c;
                }
            }

            if(!cur->child[index[*begin]]) //没有节点啊!!!
                return ;

            cur=cur->child[index[*begin]];

            begin++;

        }
        if( cur->terminable) {//是否为字符串

            if(RR[cur->id]==0){

                RR[cur->id]=r;
                CC[cur->id]=c;
            }
        }

    }

    //重载c风格
    void find(const char *str,int r,int c){

        find(str,str+strlen(str),r,c);
    }

private:

    //清空
    void clear_node(node_type cur){
        for(int i=0;i<Size;i++){
            if(cur.child[i]==0)continue; //不存在
            clear_node(*cur.child[i]);
            delete cur.childe[i];
            cur.child[i]=0;
            if(--cur.node==0) break; //没有节点了

        }

    }

    //根
    node_type root;
    //字符转索引,类似hash
    Index index;

};

class IndexClass{
public:
    int operator[](const char key){
        return key%26; //一个映射

    }

};
char cc[501][501];
char s[21];
int mini(int a,int b){
    return a>b?b:a;
}
int main(){
    trie<26,IndexClass> t;
    int R,C,i,j,l,ed;
    scanf("%d%d",&R,&C);
    getchar(); //读掉回车
    for( i=0;i<R;i++)
    {

        gets(cc[i]);
    }

    int N=0;
    while(gets(s)&&s[0]!=‘-‘){
        if(s[0]){
            t.insert(s,N);   //用每一个要查找的单词构树
                N++;
        }

    }

        for(i=0;i<R;i++)
            for( j=0;j<C;j++){
                //向下
                memset(s,0,sizeof(s));
                if(i+20<R) ed=20;
                else ed=R-i;
                for(l=0;l<ed;l++){
                    s[l]=cc[i+l][j];

                }

                t.find(s,i+1,j+1);
                //向右
                memset(s,0,sizeof(s));
                if(j+20<C) ed=20;
                else ed=C-j;
                for( l=0;l<ed;l++){
                    s[l]=cc[i][j+l];

                }

                t.find(s,i+1,j+1);

                //右下
                memset(s,0,sizeof(s));
                if(i+20<R&&j+20<C) ed=20;
                else ed=mini(C-j,R-i);
                for( l=0;l<ed;l++){
                    s[l]=cc[i+l][j+l];

                }

                t.find(s,i+1,j+1);

            }

            for( i=0;i<N;i++){

                if(RR[i]!=0||CC[i]!=0)
                    printf("%d %d\n",RR[i]-1,CC[i]-1);
                else puts("-1 -1");
            }

            return 0;

}

另外,记得有一题目是:

给你一些32位内整数(数字大概在100000左右),我们找这些数中两个使得异或值最大,以前我是没有什么头绪的,n^2算法是必超无疑问的。那现在利用字典树应该能更快地解答这个问题。首先,将数字补全到32位(前面补0)

建树的过程就是,将补全后的数字全插入,之后对于每一个数字取反后去匹配查找(如果有的话,就为1,没的话就为0,进入到有的节点继续查询),这样就可以得到一个max值了。

记得在高校俱乐部曾经有一题也是类似的,就是异或值大于K的数有多少对,当时我也没想到很好的办法,后来,其实就是在查询时候加上判断是否大于K即可。

至于实现我还没有写。因为最近在拍计算几何了,周末就是GDCPC了,很期待省赛!

Trie树基本概念和训练指南

时间: 2024-10-26 02:21:34

Trie树基本概念和训练指南的相关文章

Trie树入门及训练

什么叫Trie树? Trie树即字典树. 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希表高. (以上来自百度百科,点这里) 在我看来,Trie树是一棵26叉树(如果是统计字母的话),典型的空间换时间.那到底我们利用Trie来做什么呢? 1.统计单词 2.匹配前缀 千篇一律地需要提到

Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

HDU 1277 全文检索 (Trie树应用 好题)

全文检索 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1304    Accepted Submission(s): 416 Problem Description 我们大家经常用google检索信息,但是检索信息的程序是很困难编写的:现在请你编写一个简单的全文检索程序. 问题的描述是这样的:给定一个信息流文件,信息完全有数字组成,

Trie树详解(转)

特别声明 本文只是一篇笔记类的文章,所以不存在什么抄袭之类的. 以下为我研究时参考过的链接(有很多,这里我只列出我记得的): Trie(字典树)的应用--查找联系人 trie树 Trie树:应用于统计和排序 牛人源码,研究代码来源 1.字典树的概念 字典树,因为它的搜索快捷的特性被单词搜索系统使用,故又称单词查找树.它是一种树形结构的数据结构.之所以快速,是因为它用空间代替了速度. 2.字典树的特点: 字典树有三个基本性质: 1.根节点不包含字符,除根节点外每一个节点都只包含一个字符2.从根节点

B树、Trie树详解

查找(二) 散列表 散列表是普通数组概念的推广.由于对普通数组可以直接寻址,使得能在O(1)时间内访问数组中的任意位置.在散列表中,不是直接把关键字作为数组的下标,而是根据关键字计算出相应的下标. 使用散列的查找算法分为两步.第一步是用散列函数将被查找的键转化为数组的一个索引. 我们需要面对两个或多个键都会散列到相同的索引值的情况.因此,第二步就是一个处理碰撞冲突的过程,由两种经典解决碰撞的方法:拉链法和线性探测法. 散列表是算法在时间和空间上作出权衡的经典例子. 如果没有内存限制,我们可以直接

树结构—Trie树

很有段时间没写此系列了,今天我们来说Trie树,Trie树的名字有很多,比如字典树,前缀树等等. 一:概念 下面我们有and,as,at,cn,com这些关键词,那么如何构建trie树呢? 从上面的图中,我们或多或少的可以发现一些好玩的特性. 第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符. 第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串. 第三:每个单词的公共前缀作为一个字符节点保存. 二:使用范围 既然学Trie树,我们肯定要知道这玩意是用来干嘛的

【BZOJ3439】Kpm的MC密码 trie树+主席树

Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了... 描述 Kpm当年设下的问题是这样的: 现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串. 系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每 一个k

trie树总结

算法总结第4弹,今天来总结下字典树(Trie树),Trie树算是我学的第一个高级数据结构了吧,还是比较简单的. Trie树 Trie树,又称字典树,是字典的一种存储方式,字典中的每一个单词在Trie树种体现为从根节点出发的路径,路径中每条边代表一个字母,将边连接起来便形成了对应的单词,如图,就是一颗Trie树,其中存储了ab,ac,bc,c,cd五个单词(其中加粗节点表示单词结尾节点). 一:Trie树的基本概念 Trie树是由链接的节点所组成的数据结构,这些链接可能为空,也可能指向其他节点,每