Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )
#1014 : Trie树
描述
小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。
这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?”
身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字符串是不是这个单词的前缀不就是了?”
小Hi笑道:“你啊,还是太年轻了!~假设这本词典里有10万个单词,我询问你一万次,你得要算到哪年哪月去?”
小Ho低头算了一算,看着那一堆堆的0,顿时感觉自己这辈子都要花在上面了...
小Hi看着小Ho的囧样,也是继续笑道:“让我来提高一下你的知识水平吧~你知道树这样一种数据结构么?”
小Ho想了想,说道:“知道~它是一种基础的数据结构,就像这里说的一样!”
小Hi满意的点了点头,说道:“那你知道我怎么样用一棵树来表示整个词典么?”
小Ho摇摇头表示自己不清楚。
“你看,我们现在得到了这样一棵树,那么你看,如果我给你一个字符串ap,你要怎么找到所有以ap开头的单词呢?”小Hi又开始考校小Ho。
“唔...一个个遍历所有的单词?”小Ho还是不忘自己最开始提出来的算法。
“笨!这棵树难道就白构建了!”小Hi教训完小Ho,继续道:“看好了!”
“那么现在!赶紧去用代码实现吧!”小Hi如是说道
提示一:Trie树的建立
小Hi于是在纸上画了一会,递给小Ho,道:“你看这棵树和这个词典有什么关系?”
小Ho盯着手里的纸想了一会道:“我知道了!对于从树的根节点走到每一个黑色节点所经过的路径,如果将路径上的字母都连起来的话,就都对应着词典中的一个单词呢!”
小Hi说道:“那你知道如何根据一个词典构建这样一棵树么?”
“不造!”
“想你也不知道,我来告诉你吧~”小Hi摆出一副老师的样子,说道:“你先这么想,如果我已经有了这样的一个词典和对应的一棵树,我要添加一个新的单词apart,我应该怎么做?”
“让我想想……”小Ho又开始苦思冥想:“首先我要先看看已经能走到哪一步了对吧?比如我从1号节点走"a"这一条边就可以走到2号节点,然后从2号节点走"p"这一条边可以走到3号节点,然后……就没路可走了!这时候我就需要添加一条从3号节点出发且标记为"p"的边才可以接着往下走……最后就是这样了!然后我把最后到达的这个结点标记为黑色就可以了。”
小Hi说道:“真聪明~那你不妨再算算如果是一个有10W个单词的词典,每个单词的长度不超过10的话,这棵树会有多大?”
小Ho于是掏出笔来,一边画一遍念叨:“假设我已经将前三个单词构成了这样一棵树,那么我要添加一个新的单词的时候,最坏情况是这个单词和之前的三个单词都没有公共前缀,那么这个新的单词的长度如果是5的话,我就至少要添加5个结点到树中才能够继续表示这个词典!”
“而如果每次都是最坏情况的话,这棵树最多也就100W个结点这么大!更何况最坏情况是不可能次次都发生的!毕竟字母表也才26个字母呢!”小Ho继续说道。
“嗯~这样我们是不是就可以用(单词个数*单词长度)个结点来表示一个词典了呢?小Hi问道。
“是的呢!”小Ho道:“但是这样一棵树又有什么用呢?”
“可别小看了它,它就是传说中的Trie树哦~至于他有什么用,一会你就知道了!”小Hi笑嘻嘻的回答道。
提示二:如何使用Trie树
小Hi在树上用绿色标出了一个节点,递给小Ho。
“这个结点……是从根节点先走"a"然后走"p"到达的结点呢!哦~~我知道了,以这个结点为根的子树里所有标记结点都是以"ap"为前缀的单词呢!而且所有以"ap"为前缀的单词都在以这个节点为根的子树里~”小Ho惊喜道。
“是的呢~那你对怎么解决我的问题有想法了么?”小Hi追问道。
“唔...那就是每次拿到你的字符串之后,我在树上找到其对应的那个结点,然后统计这个节点中有多少个标记节点?”小Ho不是很确定的答道:“但是这样...似乎在最坏情况,也就是你每次给个字符串都很短的时候,我还是要扫描这棵树的很大一部分呢?也就是说虽然平均时间复杂度降低了,但是最坏情况时间复杂度还是很高的样子!”
小Hi笑嘻嘻道:”没想到你自己看出来了呢~我还以为又要教训你了!~那你有什么好的解决方法么?”
“没呢!小Hi你就别卖关子了,赶紧告诉我吧!”被折磨的够呛的小Ho开始求饶。
“好吧!就帮你这一回~”
提示三:在建立Trie树时同时进行统计!
“小Ho你有没有想过这样一个问题?不妨称以T为根的子树中标记节点的个数为L[T],既然我要统计某个L[T1],,而这个结点是不确定的,我有没有办法一次性把所有结点的L[T]求出来呢?”小Hi整理了下思绪,问道。
“似乎是有的,老师以前说过,递归什么的。。”小Ho答道。
“递归太复杂了!我们可以之后再说,你这么想,在你构建Trie树的时候,当你经过一个结点的时候,说明了什么?”小Hi撇了撇头,继续问道。
“我想想,经过一个结点……标记结点……说明了以这个结点为根的子树中将要多出来一个标记结点?”
“没错!那你有没有什么办法来记录这个改变呢?”
“我想想,我在最开始置所有L[T]=0,然后每次添加一个新的单词的时候,都将它经过的所有结点的L[T]全部+1,这样我构建完这棵Trie树的时候,我也就能够同时统计到所有L[T]了,对么?”小Ho开心道。
输入
输入的第一行为一个正整数n,表示词典的大小,其后n行,每一行一个单词(不保证是英文单词,也有可能是火星文单词哦),单词由不超过10个的小写英文字母组成,可能存在相同的单词,此时应将其视作不同的单词。接下来的一行为一个正整数m,表示小Hi询问的次数,其后m行,每一行一个字符串,该字符串由不超过10个的小写英文字母组成,表示小Hi的一个询问。
在20%的数据中n, m<=10,词典的字母表大小<=2.
在60%的数据中n, m<=1000,词典的字母表大小<=5.
在100%的数据中n, m<=100000,词典的字母表大小<=26.
本题按通过的数据量排名哦~
输出
对于小Hi的每一个询问,输出一个整数Ans,表示词典中以小Hi给出的字符串为前缀的单词的个数。
- 样例输入
-
- 5
- babaab
- babbbaaaa
- abba
- aaaaabaa
- babaababb
- 5
- babb
- baabaaa
- bab
- bb
- bbabbaab
- 5
- 样例输出
-
- 1
- 0
- 3
- 0
- 0
- 算法分析:标准的Trie算法训练题目,因为Search()函数的一个返回判断条件的错误,导致好长时间的debug!
- 差点影响了自己对指针使用的信心,实训的时候不愿意听老师讲课,一气写了好多指针实现的算法应用。至少
对于一级指针的使用还是可以的!
下午分析了好长时间的 刘汝佳《训练指南》里的Trie数组实现算法。不是很懂,有待继续!最后还是决定指针来写。
代码:
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <algorithm>
- using namespace std;
- struct node
- {
- int flag; //用来存储经过这个节点的路径(单词)
- struct node *next[26];
- };
- struct node *newnode() //这是要创建的整个字典树的根节点的开始
- {
- int i;
- struct node *p;
- p=new struct node;
- for(i=0; i<26; i++ )
- {
- p->next[i] = NULL;
- }
- p->flag=0; //这是用来标记用的
- return p;
- };
- void insert(struct node *root, char *s )
- {
- struct node *p=root;//此处必须要替换一下 目的是保证主函数里的p指针是头指针不变
- int len, i;
- int t;
- len=strlen(s);
- struct node *temp;
- for(i=0; i<len; i++)
- {
- t=s[i]-'a';
- if(p->next[t]==NULL)
- {
- temp=newnode();
- temp->flag++;
- //printf("%d--", temp->flag );
- p->next[t]=temp;
- p=p->next[t];
- }
- else
- {
- p=p->next[t];
- p->flag++;
- // printf("%d**", p->flag );
- }
- }
- //printf("\n");
- }
- int Search(struct node *root, char *t )
- {
- int i, tt;
- int len=strlen(t);
- struct node *p=root;
- int cnt;
- for(i=0; i<len; i++)
- {
- tt=t[i]-'a';
- if(p->next[tt]!=NULL )
- {
- p=p->next[tt];
- cnt=p->flag;
- //printf("%d--%d**", p->flag, cnt );
- }
- else
- break;
- }
- if( i==len )
- return cnt;
- else
- return 0;
- }
- struct node *p;
- int main()
- {
- int n, m;
- scanf("%d", &n);
- int i, j;
- char s[20], t[20];
- p=newnode();
- for(i=0; i<n; i++)
- {
- scanf("%s", s);
- insert(p, s);
- }
- scanf("%d", &m);
- for(i=0; i<m; i++)
- {
- scanf("%s", t);
- int k = Search(p, t);
- printf("%d\n", k );
- }
- return 0;
- }
- 1
Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )的更多相关文章
- Go-常识补充-切片-map(类似字典)-字符串-指针-结构体
目录 Go 常识补充 Go 命名 打印变量类型科普 _ 关键字 命名规范相关 包目录规范 切片 多维切片 切片初始化的方法 多维切片初始化 切片删除元素(会略微影响效率 ,少用) copy 函数 打散 ...
- codevs——4189 字典&&HihoCoder #1014 : Trie树
题目描述 Description 最经,skyzhong得到了一本好厉害的字典,这个字典里整整有n个单词(1<=n<=200000) 现在skyzhong需要在字典里查询以某一段字母开头的 ...
- hihoCoder 1014 : Trie树(字典树)
传送门 Description 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小 ...
- hihoCoder 1014 Trie树 (Trie)
#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 小Hi和小Ho是一对好朋友.出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮 ...
- hihoCoder #1014 : Trie树 [ Trie ]
传送门 #1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互 ...
- hihoCoder#1014 Trie树 (前缀树)
题目大意:给一本有n个单词的词典,有m次询问,每次询问的是该词典中有多少个单词有共同的某个前缀. 题目分析:在添加单词建立trie的时候,每经过一个节点就意味着该节点和它的各级祖先节点是某个单词的前缀 ...
- hihocoder 1014: Trie树(Trie树模板题)
题目链接 #include<bits/stdc++.h> using namespace std; ; struct T { int num; T* next[]; T() { num=; ...
- [hihoCoder]1014 Trie水题
最最最最最最最基本的Trie词频统计应用了. package trie; import java.util.Scanner; public class Main { public static voi ...
- #1014 : Trie树 HihoCoder(字典树)
描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题: ...
随机推荐
- cf615D Multipliers
Ayrat has number n, represented as it's prime factorization pi of size m, i.e. n = p1·p2·...·pm. Ayr ...
- 洛谷 [P3150] pb的游戏
博弈论基础 本题可以视作P2148 E&D 的前置技能 本题直接判断奇偶性来求解, 证明就是2148 的证明 不贴代码
- Spring框架 JdbcTemplate类 @Junit单元测试,可以让方法独立执行 如:@Test
package cn.zmh.PingCe; import org.junit.Test; import org.springframework.jdbc.core.BeanPropertyRowMa ...
- Spring实战Day7面向切面编程术语介绍
#### 面向切面编程 为什么需要切面? 有些功能需要在应用中的多个地方使用到,但是我们又不想在着每个地方都调用他们 切面术语 通知(advice):切面需要完成的工作 通知的类型(什么时间完成工作) ...
- MySQL---笔记之视图的使用详解
什么是视图 视图是从一个或多个表中导出来的表,是一种虚拟存在的表. 视图就像一个窗口,通过这个窗口可以看到系统专门提供的数据. 这样,用户可以不用看到整个数据库中的数据,而之关心对自己有用的数据. ...
- ScSPM
Linear Spatial Pyramid Matching using Sparse Coding for Image Classification (CVPR'09) 稀疏编码系列: (一)-- ...
- SolidEdge 工程图中如何绘制中断视图
右击长条形的视图,点击新增断裂线,然后绘制两个断点 点击完成之后效果如下图所示 如果要修改断裂视图的样式,则选中这个视图,在左键单击,然后点击这个按钮取消显示断裂视图 然后左键单击断裂视图 ...
- win10 UWP 申请微软开发人员
申请微软开发人员能够到https://dev.windows.com/zh-cn/programs/join 假设是学生,先去http://www.dreamspark.com/ 假设是英文,点stu ...
- HDU 1085 Holding Bin-Laden Captive!(母函数,或者找规律)
Holding Bin-Laden Captive! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...
- 基本SCTP套接字编程常用函数
sctp_bindx函数:允许SCTP套接字捆绑一个特定地址子集 #include <netinet/sctp.h> // 若成功返回0,出错返回-1 int sctp_bindx(int ...