字典树模板( 指针版 && 数组版 )

模板 : 

#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26;

struct Trie
{
    Trie *Next[maxn];
    int v;
    inline void init(){
        this->v = 1;
        for(int i=0; i<maxn; i++)
            this->Next[i] = NULL;
    }
};
Trie *root = (Trie *)malloc(sizeof(Trie));

void CreateTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root, *tmp;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';

        if(p->Next[idx] == NULL){
            tmp = (Trie *)malloc(sizeof(Trie));
            tmp->init();
            p->Next[idx] = tmp;
        }else p->Next[idx]->v++;

        p = p->Next[idx];
    }
    p->v = -1;//若为结尾,则将v改成-1,当然字典树里面的变量都是要依据题目
              //设置并且在特定位置赋值的
}

int FindTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';
        p = p->Next[idx];
        //...进行一系列操作
    }
}

inline void DelTrie(Trie *T)
{
    if(T == NULL) return ;
    for(int i=0; i<maxn; i++){
        if(T->Next[i] != NULL)
            DelTrie(T->Next[i]);
    }
    free(T);
    return ;
}

int main(void)
{
    root.init();//!!!
    //...
}
指针版
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26 + 1;

struct Trie
{
    int Next[maxn], v;
    inline void init(){
        v = 1;
        memset(Next, -1, sizeof(Next));
    }
};

struct Trie Node[1000000]; // 字典树可能最多的节点数,注意开足了!

int tot = 0;

void CreateTrie(char *str)
{
    int len = strlen(str);
    int now = 0;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';
        int nxt = Node[now].Next[idx];
        if( nxt == -1){
            nxt = ++tot;
            Node[nxt].init();
            Node[now].Next[idx] = nxt;
        }else Node[nxt].v++;
        now = nxt;
    }
    // Node[now].v = //尾部标志
}

int FindTrie(char *str)
{
    int len = strlen(str);
    int now = 0;
    int nxt;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';
        if(Node[now].Next[idx] != -1) now = Node[now].Next[idx];
        else return 0;
    }
    return Node[now].v;//返回该返回的值
}
数组版

 

字典树算法参考 ==> http://www.cnblogs.com/tanky_woo/archive/2010/09/24/1833717.html

 IOI论文《浅析字母树在信息学竞赛中的应用》 ==> http://www.doc88.com/p-434727490439.html

 

相关题目 : 

HUD 1251

题意 : 给出很多单词(只有小写字母组成,不会有重复的单词出现),要求统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).

分析 : 模板题,直接累加前缀即可

///数组版
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26 + 1;

struct Trie
{
    int Next[maxn], v;
    inline void init(){
        v = 1;
        memset(Next, -1, sizeof(Next));
    }
};

struct Trie Node[1000000]; // 字典树可能最多的节点数,注意开足了!

int tot = 0;

void CreateTrie(char *str)
{
    int len = strlen(str);
    int now = 0;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';
        int nxt = Node[now].Next[idx];
        if( nxt == -1){
            nxt = ++tot;
            Node[nxt].init();
            Node[now].Next[idx] = nxt;
        }else Node[nxt].v++;
        now = nxt;
    }
}

int FindTrie(char *str)
{
    int len = strlen(str);
    int now = 0;
    int nxt;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';
        if(Node[now].Next[idx] != -1) now = Node[now].Next[idx];
        else return 0;
    }
    return Node[now].v;
}


char s[11];
int main(void)
{
    Node[0].init();
    while(gets(s)){
        if(s[0] == '\0') break;
        CreateTrie(s);
    }
    while(scanf("%s", s)!=EOF){
        printf("%d\n",FindTrie(s));
    }
    return 0;
}

///指针版
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26;

struct Trie
{
    Trie *Next[maxn];
    int v;
    inline void init(){
        this->v = 1;
        for(int i=0; i<maxn; i++)
            this->Next[i] = NULL;
    }
};
Trie *root = (Trie *)malloc(sizeof(Trie));


inline void CreateTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root, *tmp;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';

        if(p->Next[idx] == NULL){
            tmp = (Trie *)malloc(sizeof(Trie));
            tmp->init();
            p->Next[idx] = tmp;
        }else p->Next[idx]->v++;

        p = p->Next[idx];
    }
    //p->v = -1;
}



int FindTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root;
    for(int i=0; i<len; i++){
        int idx = str[i]-'a';
        if(p->Next[idx] != NULL) p = p->Next[idx];
        else return 0;
    }
    return p->v;
}

inline void DelTrie(Trie *T)
{
    if(T == NULL) return ;
    for(int i=0; i<maxn; i++){
        if(T->Next[i] != NULL)
            DelTrie(T->Next[i]);
    }
    free(T);
    return ;
}
char s[11];
int main(void)
{
    root->init();
    while(gets(s)){
        if(s[0] == '\0') break;
        CreateTrie(s);
    }

    while(scanf("%s", s)!=EOF){
        printf("%d\n",FindTrie(s));
    }DelTrie(root);
    return 0;
}
View Code

 

HDU 2846

题意 : 给出 P 个单词和 Q 个询问,对于每一次询问给出询问串是多少个 P 中字符的子串

分析 : 利用KMP的话可以做,但是复杂度爆炸,可以利用字典树,将 P 个单词的每一个以及其子串全部丢去建树,最后问询串直接去跑字典树查询即可。但是在字典树上权值的累加需要注意,例如 abb 这个串,在分解的时候 b 即属于 bb 也属于 b 那么当问询 b 这个字符串的时候就应该输出 2 吗?很显然不是,对于当前分解的串,我们需要给它加一个 id 来判断当前的字符是否是由同一个串分解而来的,就能避免重复计算。

#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<malloc.h>
using namespace std;
const int maxn = 26;

struct Trie{ Trie *Next[maxn]; int v, who; };
Trie *root = (Trie *)malloc(sizeof(Trie));



void CreateTrie(char *str, int who)
{
    int len = strlen(str);
    root->who = who;
    for(int st=0; st<=len-1; st++){
        Trie *p = root, *tmp;
        for(int i=st; i<len; i++){
            int id = str[i] - 'a';

            if(p->Next[id]==NULL){
                tmp = (Trie *)malloc(sizeof(Trie));
                tmp->v = 1;
                tmp->who = who;
                for(int j=0; j<maxn; j++)
                    tmp->Next[j] = NULL;

                p->Next[id] = tmp;
                p = p->Next[id];

            }else{
                if(who != p->Next[id]->who) {
                    p->Next[id]->v++;
                    p->Next[id]->who = who;
                }
                p = p->Next[id];
            }
        }
    }

}

int ans = 0;

bool TrieFind(int n, char *str) //代码将演示在树上找str的前缀是否存在
{
    ans = 0;
    int len = strlen(str);
    Trie *p = root;
    for(int i=0; i<len; i++){
        int id = str[i] - 'a';
        if(p->Next[id] != NULL) p = p->Next[id];
        else return false;
    }
    ans = p->v;
    return true;
}

inline void DelTrie(Trie *T)
{
    int i;
    if(T == NULL) return ;
    for(int i=0; i<maxn; i++){
        if(T->Next[i] != NULL){
            DelTrie(T->Next[i]);
        }
    }
    free(T);
    return ;
}
char str[100];
int main(void)
{
    for(int i=0; i<maxn; i++) root->Next[i] = NULL;
    root->who = -1;
    root->v = 0;
    int n, q;
    scanf("%d", &n);
    for(int i=0; i<n; i++){
        scanf("%s", str);
        CreateTrie(str, i);
    }
    scanf("%d", &q);
    for(int i=0; i<q; i++){
        scanf("%s", str);
        if(TrieFind(n, str)) printf("%d\n", ans);
        else puts("0");
    }
    DelTrie(root);
    return 0;
}
View Code

 

posted @ 2017-09-27 23:24  qwerity  阅读(298)  评论(0编辑  收藏  举报