Trie树学习

来源:互联网 发布:淘宝bug是什么意思 编辑:程序博客网 时间:2024/05/22 02:11

最近重看了下KMP,然后学习AC自动机、DFA(Trie图),但发现还没有做过Trie树的题目。于是做了四五题简单的Trie树。

Tire树,发音“Try树”,也就是字典树。建立和查询的方式,类似我们建造字典和查询字典。

比如she he her his share

查找her,我们会找到h开头的那部分,然后找到he开头的部分,最后找到这个单词her。当然,建立字典也是类似的,只是不是查看而是插入。


通过四五道练习,不仅仅熟悉了Trie树,更重要的是发现实现Trie树,用动态分配内存和静态分配内存性能上显著不同


当然,结论是,静态分配会高效很多,但用了一些全局变量,不熟悉的情况下容易出错。熟悉了就没问题了。

动态分配,对于有多个测试实例,如果不释放动态分配的内存,可能导致MLE!比如这题:http://acm.hdu.edu.cn/showproblem.php?pid=1671

当然,测试实例不多,不释放也没有问题,比这题:http://poj.org/problem?id=2945

但是,释放动态分配的内存,一般就是DFS释放整棵树,还是比较消耗时间的。

比如poj2945——

动态分配&&不释放内存   ——内存消耗:54460K   时间消耗:2204MS

动态分配&&每次释放内存——内存消耗: 7932K   时间消耗:3672MS

静态分配                        ——内存消耗: 4548K   时间消耗:  532MS


下面给出poj2945的Trie树解法 及其 动态分配实现、静态分配实现


代码比较搓,先这样吧,以后再修改


静态分配实现——

//poj2945//Trie树解法,静态分配实现#include <iostream>#include <cstdio>#include <cstring>using namespace std;int const childnum = 4;const int map[] = {0, -1, 1, -1, -1, -1, 2,-1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, 3};struct node {node* child[childnum];int times;bool isstop;};const int peoplenum = 20010;const int DNAlen = 21;node Tree[peoplenum*DNAlen];int nodecnt = 0;int rec[peoplenum]; //若rec[9] = 3 表示重复了9次的不同基因有3个char str[peoplenum][DNAlen];//内联会稍微快点inline void init(node *p){memset(p->child, NULL, sizeof (p->child));p->isstop = false;p->times = 0;}void insert(node *p, char *s){int rec = nodecnt;for (int i = 0; s[i]; i++) {int idx = map[s[i]-'A'];if (p->child[idx] == NULL) {nodecnt++; //notice!!!p->child[idx] = Tree + nodecnt;init(p->child[idx]); //notice!!!}p = p->child[idx];}if (p->isstop == true) {(p->times++);return;}p->isstop = true;p->times = 1;}int main(){int n, m;while (scanf("%d%d", &n, &m), n || m) {node *p = Tree; //notice!!!init(p); //notice!!!nodecnt = 0; //notice!!!for (int i = 0; i != n; i++) {scanf("%s", str[i]);insert(p, str[i]);}memset(rec, 0, sizeof (rec));for (int i = 1; i != nodecnt+1; i++) {if (Tree[i].isstop = true) {rec[Tree[i].times]++;}}for (int i = 1; i != n+1; i++) {printf("%d\n", rec[i]);}}return 0;}


动态分配——

//poj2945//Trie树解法——动态分配实现//数据量明确,完全可以用静态分配空间,但每个测试实例要记得初始化原来的树(树根和新结点)//数据量不明确,则动态分配空间,每个测试实例的Trie树用完后要释放空间,否则可能MLE//Trie树的时间消耗——//静态建树更快,动态比较慢//Trie树的初始化——//都不能在旧树上继续建立,注意主函数,每个实例要在一个空树上(child[i]全为NULL)开始//Trie树的空间消耗——//静态建树可以始终Tree数组上建立(但注意全局变量nodecnt,每个结点的初始化)//动态建树如果不free掉旧树,可能MLE,但是free掉旧树会消耗相当一部分时间!//结论是静态建树好,但是如果每个实例大小不清楚呢?根据题目的内存限制把数组尽量开大!#include <iostream>#include <cstdio>#include <cstring>using namespace std;int const childnum = 4;const int map[] = {0, -1, 1, -1, -1, -1, 2,-1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, 3};struct node {node* child[childnum];int times;bool isstop;node(): times(0), isstop(false) {memset(child, NULL, sizeof (child));}};const int peoplenum = 20010;const int DNAlen = 21;int rec[peoplenum];char str[peoplenum][DNAlen];node* save_stop_node[peoplenum];int k = 0; //index for save_stop_nodevoid free(node *r){for (int i = 0; i != childnum; i++) {if (r->child[i] != NULL)free(r->child[i]);}delete r;}void insert(node *p, char *s){for (int i = 0; s[i]; i++) {int idx = map[s[i]-'A'];if (p->child[idx] == NULL) {p->child[idx] = new node;}p = p->child[idx];}if (p->isstop == true) {(p->times++);return;}p->isstop = true;save_stop_node[k++] = p;p->times = 1;}int main(){int n, m;while (scanf("%d%d", &n, &m), n || m) {node *r = new node;for (int i = 0; i != n; i++) {scanf("%s", str[i]);insert(r, str[i]);}memset(rec, 0, sizeof (rec));for (int i = 0; i != k; i++)rec[save_stop_node[i]->times]++;for (int i = 1; i != n+1; i++) {printf("%d\n", rec[i]);}free(r); //POJ2945,把free(r)注释掉运行更快,但内存消耗变大,还好不会MLEk = 0;}return 0;}

再附几个题解

hdoj1671

//hdoj1671 Phone List//2013-5-21//静态分配//性能:2684K 78MS#include <iostream>#include <cstdio>#include <cstring>int const childnum = 10;struct node {node *child[childnum];bool isstop;};inline void init(node *p){memset(p->child, NULL, sizeof (p->child));p->isstop = false;}int nodecnt = 0; //统计当前实例Tree树除树根的结点个数const int size = 1000000;node Tree[size];int insert(node *p, char* s){int rec = nodecnt;for (int i = 0; s[i] != '\0'; i++) {int idx = s[i]-'0';if (p->child[idx] == NULL) {if (p->isstop) //ex. 911 91152return 0;nodecnt++;p->child[idx] = Tree + nodecnt;init(p->child[idx]);//每个结点都要初始化}p = p->child[idx];}p->isstop = true;return nodecnt == rec ? 0 : 1; //ex. 91152 911}int main(){int t, n, flag;char number[15];scanf("%d", &t);while (t--) {scanf("%d", &n);flag = 1;node *r = Tree;nodecnt = 0; //记得每次新树建前nodecnt清零init(Tree); //记得每次给树根初始化while (n--) {scanf("%s", number);if (flag == 1)flag = insert(r, number);}printf(flag == 1 ? "YES\n" : "NO\n");}return 0;}



hdoj1277

#include <iostream>#include <cstdio>using namespace std;const int numsize = 10;struct node {    node *child[numsize];    int num;};int nodecnt = 0;node Tree[1000000];inline void init(node *p){    memset(p->child, NULL, sizeof (p->child));    p->num = -1;}void insert(node* p, char *s, int count){    for (int i = 0; s[i] != '\0'; i++) {        int idx = s[i]-'0';        if (p->child[idx] == NULL) {            nodecnt++;            p->child[idx] = Tree + nodecnt;            init(p->child[idx]);        }        p = p->child[idx];    }    p->num = count;}int mark[10010];void search(node *r, char *s){    node *p = r;        for (int i = 0; s[i] != '\0'; i++) {        int idx = s[i]-'0';        if (p->num != -1 && mark[p->num] == 0) {            printf(" [Key No. %d]", p->num);            mark[p->num] = 1;        }        if (p->child[idx] == NULL)            return;        p = p->child[idx];    }}char Text[60010];char tmp[10];char key[65];int main(){    int n, m;    int cnt = 0, k = 0;    char c;        scanf("%d%d%*c", &n, &m);    getchar();    while (c = getchar()) {        if (c == '\n')            cnt++;        else            Text[k++] = c;        if (cnt == n)            break;    }    Text[k] = '\0';        node *r = Tree;    init(r);    nodecnt = 0;        for (int i = 1; i != m+1; i++) {        scanf("%s%s%s%s", tmp, tmp, tmp, key);        insert(r, key, i);    }        printf("Found key:");    for(int i=0; Text[i] != '\0'; i++) {        search(r, Text+i);    }    printf("\n");    return 0;}


这个题目需要考虑的比较全面,比如关键字是11,全文中可能多次出现!再比如出现一个关键字是另一个关键字的后缀,比如1314515151和515151
另外这个题目的测试数据全都是可以找到key的!





原创粉丝点击