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;}
另外这个题目的测试数据全都是可以找到key的!
- Trie树与Trie图的学习
- Trie树学习
- Trie 树学习下
- Trie树学习1
- Trie树学习2
- trie树 整理学习
- Trie树学习
- Trie树学习记录
- Trie树的学习
- Trie树学习笔记
- C# 学习笔记:Trie树
- 算法学习之Trie树
- Trie树学习--数据结构一发
- Trie(字典树)的学习
- hihoCoder1014 Trie树 [Trie]
- 树学习 ---------字典树(Trie Tree)
- 【学习记录】trie树(字典树)
- Trie(字典树)学习小结1
- Linux网络协议栈之数据包处理过程
- 黑马程序员-JAVASE入门(Map集合)
- HDU 1217 Arbitrage
- WCF - WCF 4.0 新特性
- Android系统手机端抓包方法【详解】
- Trie树学习
- linux建立yum仓库
- Linux Netcat 命令—网络工具中的瑞士军刀
- 解析linux根文件系统的挂载过程
- 循环冗余校验码(CRC)
- C++常见笔试题
- vba excel电话号码的导入(sheet1 to sheet2)
- 传智播客.Net培训加入工作流技术
- 查看一些开源程序的源码的网站