Tire树(字典树)
Posted jiamian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tire树(字典树)相关的知识,希望对你有一定的参考价值。
from:https://www.cnblogs.com/justinh/p/7716421.html
Trie,又经常叫前缀树,字典树等等。它有很多变种,如后缀树,Radix Tree/Trie,PATRICIA tree,以及bitwise版本的crit-bit tree。当然很多名字的意义其实有交叉。
定义
在计算机科学中,trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。
trie中的键通常是字符串,但也可以是其它的结构。trie的算法可以很容易地修改为处理其它结构的有序序列,比如一串数字或者形状的排列。比如,bitwise trie中的键是一串位元,可以用于表示整数或者内存地址
基本性质
1,根节点不包含字符,除根节点意外每个节点只包含一个字符。
2,从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
3,每个节点的所有子节点包含的字符串不相同。
优点:
可以最大限度地减少无谓的字符串比较,故可以用于词频统计和大量字符串排序。
跟哈希表比较:
1,最坏情况时间复杂度比hash表好
2,没有冲突,除非一个key对应多个值(除key外的其他信息)
3,自带排序功能(类似Radix Sort),先序遍历trie可以得到排序。
缺点:
1,虽然不同单词共享前缀,但其实trie是一个以空间换时间的算法。其每一个字符都可能包含至多字符集大小数目的指针(不包含卫星数据)。
每个结点的子树的根节点的组织方式有几种。1>如果默认包含所有字符集,则查找速度快但浪费空间(特别是靠近树底部叶子)。2>如果用链接法(如左儿子右兄弟),则节省空间但查找需顺序(部分)遍历链表。3>alphabet reduction: 减少字符宽度以减少字母集个数。,4>对字符集使用bitmap,再配合链接法。
2,如果数据存储在外部存储器等较慢位置,Trie会较hash速度慢(hash访问O(1)次外存,Trie访问O(树高))。
3,长的浮点数等会让链变得很长。可用bitwise trie改进。
bit-wise Trie
类似于普通的Trie,但是字符集为一个bit位,所以孩子也只有两个。
可用于地址分配,路由管理等。
虽然是按bit位存储和判断,但因为cache-local和可高度并行,所以性能很高。跟红黑树比,红黑树虽然纸面性能更高,但是因为cache不友好和串行运行多,瓶颈在存储访问延迟而不是CPU速度。
压缩Trie
压缩分支条件:
1,Trie基本不变
2,只是查询
3,key跟结点的特定数据无关
4,分支很稀疏
若允许添加和删除,就可能需要分裂和合并结点。此时可能需要对压缩率和更新(裂,并)频率进行折中。
外存Trie
某些变种如后缀树适合存储在外部,另外还有B-trie等。
应用场景:
(1) 字符串检索
事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。
举例:
1,给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
2,给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。
3,1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。
(2)文本预测、自动完成,see also,拼写检查
(3)词频统计
1,有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
2,一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
3,寻找热门查询:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复度比较高,虽然总数是1千万,但是如果去除重复,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1) 请描述你解决这个问题的思路;
(2) 请给出主要的处理流程,算法,以及算法的复杂度。
==》若无内存限制:Trie + “k-大/小根堆”(k为要找到的数目)。
否则,先hash分段再对每一个段用hash(另一个hash函数)统计词频,再要么利用归并排序的某些特性(如partial_sort),要么利用某使用外存的方法。参考
“海量数据处理之归并、堆排、前K方法的应用:一道面试题” http://www.dataguru.cn/thread-485388-1-1.html。
“算法面试题之统计词频前k大” http://blog.csdn.net/u011077606/article/details/42640867
(4)排序
Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。
比如给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出。
(5)字符串最长公共前缀
Trie树利用多个字符串的公共前缀来节省存储空间,当我们把大量字符串存储到一棵trie树上时,我们可以快速得到某些字符串的公共前缀。
举例:
给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少?
解决方案:首先对所有的串建立其对应的字母树。此时发现,对于两个串的最长公共前缀的长度即它们所在结点的公共祖先个数,于是,问题就转化为了离线(Offline)的最近公共祖先(Least Common Ancestor,简称LCA)问题。
而最近公共祖先问题同样是一个经典问题,可以用下面几种方法:
1. 利用并查集(Disjoint Set),可以采用采用经典的Tarjan 算法;
2. 求出字母树的欧拉序列(Euler Sequence )后,就可以转为经典的最小值查询(Range Minimum Query,简称RMQ)问题了;
(6)字符串搜索的前缀匹配
trie树常用于搜索提示。如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。
Trie树检索的时间复杂度可以做到n,n是要检索单词的长度,
如果使用暴力检索,需要指数级O(n2)的时间复杂度。
(7) 作为其他数据结构和算法的辅助结构
如后缀树,AC自动机等
后缀树可以用于全文搜索
转一篇关于几种Trie速度比较的文章:http://www.hankcs.com/nlp/performance-comparison-of-several-trie-tree.html
Trie树和其它数据结构的比较 http://www.raychase.net/1783
参考:
[1] 维基百科:Trie, https://en.wikipedia.org/wiki/Trie
[2] LeetCode字典树(Trie)总结, http://www.jianshu.com/p/bbfe4874f66f
[3] 字典树(Trie树)的实现及应用, http://www.cnblogs.com/binyue/p/3771040.html#undefined
[4] 6天通吃树结构—— 第五天 Trie树, http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html
在Trie树中主要有3个操作,插入、查找和删除。一般情况下Trie树中很少存在删除单独某个结点的情况,因此只考虑删除整棵树。
例题及思路
统计难题
Problem Description
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
Sample Output
2 3 1 0
统计出以某个字符串为前缀的单词数量,首先构建出trie树并记录每个节点的访问次数,然后在上面查询就好了,模板题。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <algorithm> 5 #define MAXN 26 6 using namespace std; 7 8 struct Trie 9 10 Trie *Next[MAXN]; 11 int Flag; 12 Trie() 13 14 Flag=1; 15 memset(Next,NULL,sizeof(Next)); 16 17 ; 18 19 struct Trie* Root; 20 21 void Insert(char* str) 22 23 Trie *p,*q; 24 p=Root; 25 int len=strlen(str); 26 for(int i=0;i<len;++i) 27 28 int key=str[i]-‘a‘; 29 if(p->Next[key]==NULL) 30 31 q=new Trie(); 32 p->Next[key]=q; 33 p=p->Next[key]; 34 35 else 36 37 p=p->Next[key]; 38 ++p->Flag; 39 40 41 42 43 int Qurey(char *str) 44 45 int len=strlen(str); 46 Trie* p=Root; 47 for(int i=0;i<len;++i) 48 49 int key=str[i]-‘a‘; 50 if(p->Next[key]==NULL) 51 return 0; 52 p=p->Next[key]; 53 54 return p->Flag; 55 56 57 58 void Free(Trie* T) 59 60 if(T==NULL) return; 61 for(int i=0;i<MAXN;i++) 62 63 if(T->Next[i]) Free(T->Next[i]); 64 65 delete(T); 66 67 68 int main() 69 70 //freopen("sample.txt","r",stdin); 71 char str[15]; 72 Root=new Trie(); 73 while(*gets(str)) //效果等同于while(gets(str)&&str[0]!=0) 74 75 Insert(str); 76 77 while(~scanf("%s",str)) 78 79 printf("%d\\n",Qurey(str)); 80 81 Free(Root); 82 return 0; 83
单词数
Problem Description
lily的好朋友xiaoou333最近很空,他想了一件没有什么意义的事情,就是统计一篇文章里不同单词的总数。下面你的任务是帮助xiaoou333解决这个问题。
Input
有多组数据,每组一行,每组就是一篇小文章。每篇小文章都是由小写字母和空格组成,没有标点符号,遇到#时表示输入结束。
Output
每组只输出一个整数,其单独成行,该整数代表一篇文章里不同单词的总数。
Sample Input
you are my friend
#
Sample Output
4
统计出现的不同单词的个数,直接把字符全部插入Trie树中,然后遍历trie树,统计所有具有End标记的节点个数就好了。
提示:多样例输入的格式是第一种,用字符数组会被卡
这里可以用 istringstream来快速从字符串中根据空格来提取单词,istringstream对象可以绑定一行字符串,然后以空格为分隔符把该行分隔开来。(要加头文件sstream)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <sstream> 6 #define MAXN 26 7 using namespace std; 8 9 struct Trie 10 11 Trie *Next[MAXN]; 12 int flag; 13 int end; 14 Trie() 15 16 flag=1; 17 end=0; 18 memset(Next,NULL,sizeof(Next)); 19 20 *Root; 21 22 void Insert(string str) 23 24 Trie *p=Root; 25 Trie *q; 26 int len=str.size(); 27 for(int i=0;i<len;++i) 28 29 int key=str[i]-‘a‘; 30 if(p->Next[key]==NULL) 31 32 q=new Trie(); 33 p->Next[key]=q; 34 p=p->Next[key]; 35 36 else 37 38 p=p->Next[key]; 39 ++p->flag; 40 41 if(i==len-1) 42 p->end=1; 43 44 45 46 int visit(Trie* T,int &sum) //遍历trie树 47 48 if(T==NULL) 49 return 0; 50 if(T->end==1) 51 sum++; 52 for(int i=0;i<MAXN;i++) 53 54 visit(T->Next[i],sum); 55 56 57 58 void Free(Trie* T) 59 60 if(T==NULL) return; 61 for(int i=0;i<MAXN;i++) 62 63 if(T->Next[i]) Free(T->Next[i]); 64 65 delete(T); 66 67 68 int main() 69 70 string str; 71 string tem; //字符数组被卡,改用string就过了。。。。 72 int sum=0; 73 Root=new Trie(); 74 while(getline(cin,str)&&str!="#") 75 76 istringstream is(str); //is相当于存单词的一个容器 77 while(is>>tem) //将is中的字符串依次赋给string 78 79 Insert(tem); 80 81 visit(Root,sum); 82 printf("%d\\n",sum); 83 Free(Root); 84 Root=new Trie(); 85 sum=0; 86 87 Free(Root); 88 return 0; 89
Shortest Prefixes
Description
In the sample input below, "carbohydrate" can be abbreviated to "carboh", but it cannot be abbreviated to "carbo" (or anything shorter) because there are other words in the list that begin with "carbo".
An exact match will override a prefix match. For example, the prefix "car" matches the given word "car" exactly. Therefore, it is understood without ambiguity that "car" is an abbreviation for "car" , not for "carriage" or any of the other words in the list that begins with "car".
Input
Output
Sample Input
carbohydrate
cart
carburetor
caramel
caribou
carbonic
cartilage
carbon
carriage
carton
car
carbonate
Sample Output
carbohydrate carboh
cart cart
carburetor carbu
caramel cara
caribou cari
carbonic carboni
cartilage carti
carbon carbon
carriage carr
carton carto
car car
carbonate carbona
求一个能代表这个字符串的最短前缀,也就是只有这个字符串具有的前缀,否则输出这个字符串本身
做法很显然,先构建好Trie树,然后对每个单词进行find,递归到直到节点出现次数为1,表示这个节点只有这一个单词走过,返回就ok。
这里可以用string不断拼接字符,然后直接返回,减少一些代码量。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 #define MAXN 26 7 const int maxn=2e6+5; 8 int Trie[maxn][MAXN]; 9 int Count[maxn]; 10 bool End[maxn]; 11 int tot; 12 13 void Find(char *str) 14 15 int len=strlen(str); 16 int Root=0; 17 int flag=1; 18 int i; 19 for(i=0;i<len;i++) 20 21 int key=str[i]-‘a‘; 22 Root=Trie[Root][key]; 23 printf("%c",str[i]); 24 if(Count[Root]==1) 25 26 printf("\\n"); 27 return; 28 29 30 printf("\\n"); 31 return ; 32 33 34 void Insert(char *str) 35 36 int len=strlen(str); 37 int Root=0; 38 for(int i=0;i<len;i++) 39 40 int key=str[i]-‘a‘; 41 if(!Trie[Root][key]) 42 43 Trie[Root][key]=++tot; 44 Count[Trie[Root][key]]=1; 45 46 else 47 48 Count[Trie[Root][key]]++; 49 50 Root=Trie[Root][key]; 51 52 End[Root]=true; 53 54 55 void init() 56 57 for(int i=0;i<tot;i++) 58 59 End[i]=false; 60 Count[i]=0; 61 for(int j=0;j<MAXN;j++) 62 63 Trie[i][j]=0; 64 65 66 tot=0; 67 68 char ss[1005][21]; 69 int main() 70 71 int num=0; 72 while(scanf("%s",ss[num])!=EOF) 73 74 Insert(ss[num++]); 75 76 for(int i=0;i<num;i++) 77 78 printf("%s ",ss[i]); 79 Find(ss[i]); 80 81 init(); 82 return 0; 83
Phone List
Description
Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let‘s say the phone catalogue listed these numbers:
- Emergency 911
- Alice 97 625 999
- Bob 91 12 54 26
In this case, it‘s not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob‘s phone number. So this list would not be consistent.
Input
The first line of input gives a single integer, 1 ≤ t ≤ 40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1 ≤ n ≤ 10000. Then follows n lines with one unique phone number on each line. A phone number is a sequence of at most ten digits.
Output
For each test case, output "YES" if the list is consistent, or "NO" otherwise.
Sample Input
2 3 911 97625999 91125426 5 113 12340 123440 12345 98346
Sample Output
NO
YES
给出一个字符串集合,问是否所有的字符串都不是其他字符串的前缀,
之前一直时间超限。。。。把链式结构换成了二维数组
然后这个竟然wa掉了,
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 #define MAXN 10 7 const int maxn=2e6+5; 8 int Trie[maxn][MAXN]; 9 bool End[maxn]; 10 int tot; 11 12 void init() 13 14 for(int i=0;i<tot;i++) 15 16 End[i]=false; 17 for(int j=0;j<MAXN;j++) 18 19 Trie[i][j]=0; 20 21 22 tot=0; 23 24 25 int main() 26 27 int t; 28 scanf("%d",&t); 29 while(t--) 30 31 int n; 32 scanf("%d",&n); 33 getchar(); 34 int flag=1; 35 while(n--) 36 37 char str[20]; 38 gets(str); 39 int len=strlen(str); 40 int Root=0; 41 for(int i=0;i<len;i++) 42 43 int key=str[i]-‘0‘; 44 if(!Trie[Root][key]) 45 Trie[Root][key]=++tot; 46 else if(End[Trie[Root][key]]==true) 47 48 flag=0; 49 50 Root=Trie[Root][key]; 51 if(i==len-1) 52 End[Root]=true; 53 54 for(int i=0;i<MAXN;i++) 55 56 if(Trie[Root][i]!=0) 57 58 flag=0; 59 break; 60 61 62 63 if(flag) 64 printf("YES\\n"); 65 else 66 printf("NO\\n"); 67 init(); 68 69 return 0; 70
能过的代码:
首先构造出Trie树,然后对每个字符串find,当find的路径上如果出现其他字符串结尾标记,就说明其他字符串是当前字符串的前缀。注意这里对每个字符串find的时候只要搜索到len−1 len-1len−1即可,如果搜索到len lenlen,那么将会将本身的字符串统计进去。
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 const int maxn =2e6+5; 6 int tree[maxn][15]; 7 bool flagg[maxn]; 8 int tot; 9 void insert_(char *str) 10 11 int len=strlen(str); 12 int root=0; 13 for(int i=0;i<len;i++) 14 15 int id=str[i]-‘0‘; 16 if(!tree[root][id]) tree[root][id]=++tot; 17 root=tree[root][id]; 18 19 flagg[root]=true; 20 21 int find_(char *str) 22 23 int len=strlen(str); 24 int root=0; 25 for(int i=0;i<len-1;i++) 26 27 int id=str[i]-‘0‘; 28 root=tree[root][id]; 29 if(flagg[root]) return true;//路径上出现过其他字符串的结尾标记 30 31 return false; 32 33 char ss[10005][12]; 34 int main() 35 36 int n,t; 37 scanf("%d",&t); 38 while(t--) 39 40 scanf("%d",&n); 41 for(int i=0;i<n;i++) 42 43 scanf("%s",ss[i]); 44 insert_(ss[i]); 45 46 int flag=0; 47 for(int i=0;i<n;i++) 48 49 if(find_(ss[i])) 50 51 flag=1; 52 break; 53 54 55 if(flag==0) 56 printf("YES\\n"); 57 else 58 printf("NO\\n"); 59 for(int i=0;i<=tot;i++) 60 61 flagg[i]=false; 62 for(int j=0;j<10;j++) 63 tree[i][j]=0; 64 65 tot=0; 66 67 return 0; 68 69 --------------------- 70 作者:lajiyuan_ 71 来源:CSDN 72 原文:https://blog.csdn.net/qq_38891827/article/details/80532462 73 版权声明:本文为博主原创文章,转载请附上博文链接!
T9
Description
A while ago it was quite cumbersome to create a message for the Short Message Service (SMS) on a mobile phone. This was because you only have nine keys and the alphabet has more than nine letters, so most characters could only be entered by pressing one key several times. For example, if you wanted to type "hello" you had to press key 4 twice, key 3 twice, key 5 three times, again key 5 three times, and finally key 6 three times. This procedure is very tedious and keeps many people from using the Short Message Service.
This led manufacturers of mobile phones to try and find an easier way to enter text on a mobile phone. The solution they developed is called T9 text input. The "9" in the name means that you can enter almost arbitrary words with just nine keys and without pressing them more than once per character. The idea of the solution is that you simply start typing the keys without repetition, and the software uses a built-in dictionary to look for the "most probable" word matching the input. For example, to enter "hello" you simply press keys 4, 3, 5, 5, and 6 once. Of course, this could also be the input for the word "gdjjm", but since this is no sensible English word, it can safely be ignored. By ruling out all other "improbable" solutions and only taking proper English words into account, this method can speed up writing of short messages considerably. Of course, if the word is not in the dictionary (like a name) then it has to be typed in manually using key repetition again.
More precisely, with every character typed, the phone will show the most probable combination of characters it has found up to that point. Let us assume that the phone knows about the words "idea" and "hello", with "idea" occurring more often. Pressing the keys 4, 3, 5, 5, and 6, one after the other, the phone offers you "i", "id", then switches to "hel", "hell", and finally shows "hello".
Problem
Write an implementation of the T9 text input which offers the most probable character combination after every keystroke. The probability of a character combination is defined to be the sum of the probabilities of all words in the dictionary that begin with this character combination. For example, if the dictionary contains three words "hell", "hello", and "hellfire", the probability of the character combination "hell" is the sum of the probabilities of these words. If some combinations have the same probability, your program is to select the first one in alphabetic order. The user should also be able to type the beginning of words. For example, if the word "hello" is in the dictionary, the user can also enter the word "he" by pressing the keys 4 and 3 even if this word is not listed in the dictionary.
Input
The first line contains the number of scenarios.
Each scenario begins with a line containing the number w of distinct words in the dictionary (0<=w<=1000). These words are given in the next w lines. (They are not guaranteed in ascending alphabetic order, although it‘s a dictionary.) Every line starts with the word which is a sequence of lowercase letters from the alphabet without whitespace, followed by a space and an integer p, 1<=p<=100, representing the probability of that word. No word will contain more than 100 letters.
Following the dictionary, there is a line containing a single integer m. Next follow m lines, each consisting of a sequence of at most 100 decimal digits 2-9, followed by a single 1 meaning "next word".
Output
The output for each scenario begins with a line containing "Scenario #i:", where i is the number of the scenario starting at 1.
For every number sequence s of the scenario, print one line for every keystroke stored in s, except for the 1 at the end. In this line, print the most probable word prefix defined by the probabilities in the dictionary and the T9 selection rules explained above. Whenever none of the words in the dictionary match the given number sequence, print "MANUALLY" instead of a prefix.
Terminate the output for every number sequence with a blank line, and print an additional blank line at the end of every scenario.
Sample Input
2 5 hell 3 hello 4 idea 8 next 8 super 3 2 435561 43321 7 another 5 contest 6 follow 3 give 13 integer 6 new 14 program 4 5 77647261 6391 4681 26684371 77771
Sample Output
Scenario #1: i id hel hell hello i id ide idea Scenario #2: p pr pro prog progr progra program n ne new g in int c co con cont anoth anothe another p pr MANUALLY MANUALLY
Hat’s Words
Problem Description
You are to find all the hat’s words in a dictionary.
Input
Only one case.
Output
Sample Input
a
ahat
hat
hatword
hziee
word
Sample Output
ahat
hatword
给出n个按字典序排列的单词,问其中的所有能由另外两个单词组成的单词并按字典序输出
(推荐)思路1:先建好字典树,然后枚举一个单词将其拆分成两个单词并查询字典树中是否有这两个单词;
思路2:先把所有的单词构造成一颗trie图,然后对所有的单词进行枚举,在trie图上面判断一个单词是否由其它两个单词构成,具有的做法是先沿着路径一直走,如果走到某个节点,该节点为一个单词的结尾,那么再对剩余的单词再从trie图的根开始遍历,看是否能和一个单词匹配,若匹配成功则该单词满足要求,否则继续进行匹配...
思路3:可以建两颗Trie树,然后分别正序倒序插入每个单词,对每个单词查询的时候,分别正序倒序查询,对出现过单词的前缀下标进行标记,对每个出现过单词的后缀进行标记,最后扫描标记数组,如果某个位置前缀后缀均被标记过,则表示可以拆成单词表中的两个其他单词。
没有想到该怎样枚举,只想选一个单词另外两个怎么选,该弄多少个循环呀,看了别人的方法才知道将这个单词拆分成两个单词;还了解了strncpy,一些字符函数能用则用,本来就是为了方便!
1 #include <cstdio> 2 #include <iostream> 3 #include <string> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <algorithm> 7 using namespace std; 8 #define MAXN 26 9 10 char word[50005][50]; 11 char ss1[50],ss2[50]; 12 int num; 13 14 struct Trie 15 int Flag; 16 Trie *Next[MAXN]; 17 Trie() 18 19 Flag=0; 20 memset(Next,NULL,sizeof(Next)); 21 22 *Root; 23 24 void Insert(char *str) 25 26 Trie *p=Root; 27 Trie *q=NULL; 28 int len=strlen(str); 29 for(int i=0;i<len;i++) 30 31 int key=str[i]-‘a‘; 32 if(!p->Next[key]) 33 34 q=new Trie(); 35 p->Next[key]=q; 36 37 p=p->Next[key]; 38 if(i==len-1) 39 p->Flag=1; 40 41 return; 42 43 44 int Qurey(char *str) 45 46 Trie *p=Root; 47 int len=strlen(str); 48 int flag=0; 49 for(int i=0;i<len;i++) 50 51 int key=str[i]-‘a‘; 52 if(!p->Next[key]) 53 54 return 0; 55 56 p=p->Next[key]; 57 58 return p->Flag; 59 60 61 void Find(char *str) 62 63 int len=strlen(str); 64 for(int m=1;m<len;m++) 65 66 //这里用strncmp 67 strncpy(ss1,str,m); 68 ss1[m]=0; 69 strncpy(ss2,str+m,len-m+1); 70 ss2[len-m+1]=0; 71 // 不用strncmp的话 72 // int j=0; 73 // for(int i=0;i<m;i++) 74 // 75 // ss1[j++]=str[i]; 76 // 77 // ss1[j]=0; 78 // j=0; 79 // for(int i=m;i<len;i++) 80 // 81 // ss2[j++]=str[i]; 82 // 83 // ss2[j]=0; 84 //printf("ss1=%s ss2=%s\\n",ss1,ss2); 85 if(Qurey(ss1)&&Qurey(ss2)) 86 87 puts(str); 88 break; //不带break;就wa掉了,无语 89 90 91 92 93 void Free(Trie *T) 94 95 if(T==NULL) return; 96 for(int i=0;i<MAXN;i++) 97 98 if(T->Next[i]) Free(T->Next[i]); 99 100 delete(T); 101 102 103 int main() 104 105 freopen("sampletem.txt","r",stdin); 106 char str[50]; 107 Root=new Trie(); 108 while(~scanf("%s",str)) 109 110 strcpy(word[num++],str); 111 Insert(str); 112 113 for(int i=0;i<num;i++) 114 115 Find(word[i]); 116 117 Free(Root); 118 return 0; 119
What Are You Talking About
Problem Description
Ignatius is so lucky that he met a Martian yesterday. But he didn‘t know the language the Martians use. The Martian gives him a history book of Mars and a dictionary when it leaves. Now Ignatius want to translate the history book into English. Can you help him?
Input
Output
Sample Input
START from fiwo hello difh mars riwosf earth fnnvk like fiiwj END START difh, i‘m fiwo riwosf. i fiiwj fnnvk! END
Sample Output
hello, i‘m from mars. i like earth!
Hint
Huge input, scanf is recommended.
本题的题意是给你火星文与地球文的映射方式,然后给你一个火星文组成的文本,若某单词在映射文本中出现过,则输出映射之后的文本。否则输出原文本。
1 #include <cstdio> 2 #include <iostream> 3 #include <string> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <algorithm> 7 using namespace std; 8 #define MAXN 26 9 string fin=""; 10 11 struct Trie 12 int Flag; 13 Trie *Next[MAXN]; 14 string chc; 15 Trie() 16 17 Flag=0; 18 memset(Next,NULL,sizeof(Next)); 19 20 *Root; 21 22 void Insert(string str,string ss) 23 24 Trie *p=Root; 25 Trie *q=NULL; 26 int len=str.size(); 27 for(int i=0;i<len;i++) 28 29 int key=str[i]-‘a‘; 30 if(!p->Next[key]) 31 32 q=new Trie(); 33 p->Next[key]=q; 34 35 p=p->Next[key]; 36 if(i==len-1) 37 p->Flag=1; 38 p->chc=ss; 39 40 return; 41 42 43 void Qurey(string str) 44 45 Trie *p=Root; 46 int len=str.size(); 47 int flag=0; 48 for(int i=0;i<len;i++) 49 50 int key=str[i]-‘a‘; 51 if(!p->Next[key]) 52 53 flag=1; 54 break; 55 56 else 57 p=p->Next[key]; 58 if(i==len-1&&p->Flag==1) 59 fin+=p->chc; 60 61 if(flag||p->Flag!=1) 62 fin+=str; 63 return; 64 65 66 int main() 67 68 //freopen("sampletem.txt","r",stdin); 69 string a,b; 70 Root=new Trie(); 71 cin>>a; 72 while(cin>>a) 73 74 if(a[0]==‘E‘) 75 break; 76 cin>>b; 77 Insert(b,a); 78 79 cin>>a; 80 getchar(); 81 char c; 82 string tem=""; 83 while(c=getchar()) 84 85 if(c==‘E‘) 86 87 c=getchar(); 88 c=getchar(); 89 break; 90 91 else if(c>=‘a‘&&c<=‘z‘) 92 93 tem+=c; 94 95 else 96 97 if(tem!="") 98 Qurey(tem); 99 fin+=c; 100 tem=""; 101 102 103 cout<<fin; 104 return 0; 105
以上是关于Tire树(字典树)的主要内容,如果未能解决你的问题,请参考以下文章