关于字典树,相信还是比较好理解的吧,就是将一次输入中的一些单词,字符串建立成字典。将这些字符每一层存放一个字符,顺序往下查找,在该个字符串(找完该字符串的最后一个字符,即最后一个节点)末尾设置一个节点(一般是存放的东西,比如要翻译过来的单词啊什么的)。
下面来分享两道典型的字典树题:
1.ZOJ 1109 Language of FatMouse:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1109
大概题意:FatMouse只会说鼠语,但是为了迎接WTO的热潮,它想学好英语,所以就搞了个类似小抄的东西。输入英语语言和鼠语,要求我们将鼠语言翻译英语输出。
这是单词的翻译,属于简单的字典树。只要将鼠语见成字典树,对应的英语单词作为节点放在鼠语结束的末尾,每次翻译是查找字典树即可。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
using namespace std;
char fwords[11];
struct Trie{
Trie *next[26]; //建树,每一层都有26个小写的英文单词(管它用不用呢,万一要全用),如果加入大写和数字,那就要开的更大了
char eword[11]; //用来储存英语(要存在节点出的,同样,管他用不用呢。
Trie(){ //树的初始化
for(int i=0;i<26;i++)
next[i]=NULL;
eword[0]='\0';
}
};
Trie root; //这里是树根,从此开始往下建树
void build(char ew[],char d[]){ //d是鼠语
Trie *now=&root; //每次建树都要从树根开始,看看初始鼠语字符是否存在
int len=strlen(d);
for(int i=0;i<len;i++){
if(now->next[d[i]-'a']==NULL) //如果没有,那就新往下建一个
now->next[d[i]-'a']=new Trie;
now=now->next[d[i]-'a']; //有那就顺着这个节点往下摸
}
strcpy(now->eword,ew);
}
char *TrieSearch(char str[]){
Trie *p=&root;
int len=strlen(str);
int i;
for(i=0;i<len&&p->next[str[i]-'a']!=NULL;i++)
p=p->next[str[i]-'a'];
if(i==len&&strlen(p->eword)!=0)
return p->eword;
return NULL;
}
int main()
{
char re[11],dic[11];
char line[30];
while(gets(line)){
if(strlen(line) == 0)break;
sscanf(line,"%s%s",re,dic);
build(re,dic);
}
while(gets(line)){
char *p=TrieSearch(line);
if(p) printf("%s\n",p);
else printf("eh\n");
}
return 0;
}
接下来是个稍微变化了一下的字典树题目:
DHOJ 1075hdoj What Are You Talking About:http://acm.hdu.edu.cn/showproblem.php?pid=1075
题意:同样也是要翻译,但是这次输入有其他的各种符号,但是不用管它,最后的时候处理就好了,给出代码:
//Trie
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <cctype>
char tmp[3000+10]; //后续输入
//树的结构体
struct Trie{
char ewards[11];
Trie *next[26];
Trie(){
for(int i=0;i<26;i++)
next[i]=NULL;
ewards[0]='\0';
}
};
Trie root; //建立字典树树根
//函数功能:建树并保存单词(即在单词结束时保存节点)
void build(char e[],char mar[]){
Trie *now=&root; //从树顶端开始建立
int len=strlen(mar);
for(int i=0;i<len;i++){ //mar是字典,直到将其全部非'\0'字符历遍
if(now->next[mar[i]-'a']==NULL)
now->next[mar[i]-'a']=new Trie;//如果该字符未被保存过,那么建立新的节点
now=now->next[mar[i]-'a'];
}
strcpy(now->ewards,e);
}
char *TrieSearch(char s[]){
Trie *p =&root;
int i; // 下边还会用到
for(i=0;s[i]!='\0'&&p->next[s[i]-'a']!=NULL;i++)
p=p->next[s[i]-'a']; //符合要求那么就继续向下
if(s[i]=='\0'&&strlen(p->ewards)!=0)//s被历遍,且最后一个树点有节点标记(此处为有单词
return p->ewards;
return NULL;
}
using namespace std;
int main()
{
char ear[11],mar[11];
scanf("%s",ear);
while(scanf("%s",ear)&&ear[0]!='E'){
scanf("%s",mar);
build(ear,mar);
}
scanf("%s",mar);scanf("\n"); //输入START,记得吞'\n'
memset(tmp,'\0',sizeof(tmp));
while(gets(tmp)!=NULL&&tmp[0]!='E'){
int len=strlen(tmp);
tmp[len]=' ';len++;
char tp[3000+10];
for(int i=0,k=0;i<len;i++){
if(tmp[i]>='a'||tmp[i]<='z'){
tp[k]='\0';
char *temp=TrieSearch(tp);
if(temp) printf("%s",temp);
else printf("%s",tp);
k=0; //从新开始保存单词或者结束
if(i!=len-1) printf("%c",tmp[i]);
}
else
tp[k++]=tmp[i];
}
printf("\n");
}
return 0;
}
不难发现,字典树对于内存上的消耗还是很大的,下面给出第一题的另一种解法,研究qsort的结构体字符串排序想了好久,再利用二分,时间上就满足了。但是这种方法有些题目还是有些不适用的。我也没想清楚。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100000+10
using namespace std;
struct words{
char ewords[11];
char ftword[11];
}arr[MAXN];
int cmp(const void *a,const void *b){
struct words *pa = (struct words *)a;
struct words *pb = (struct words *)b;
return strcmp(pa->ftword,pb->ftword);
}
char * dsearch(char id[],int low,int high){ // 排序后进行二分查找,快速定位下来
while(low<=high){
int mid=(low+high)>>1;
//cout << "str1="<< id <<" str2="<<arr[mid].ftword<<endl;
if(!strcmp(id,arr[mid].ftword)){
return arr[mid].ewords;
}
else if(strcmp(id,arr[mid].ftword)>0)
low=mid+1;
else
high=mid-1;
}
return NULL;
}
int main(){
int m=0;
char line[30];
for(m=0;gets(line)&&strlen(line);m++){
sscanf(line,"%s%s",arr[m].ewords,arr[m].ftword);
}
qsort(arr,m,sizeof(arr[0]),cmp);
while(gets(line)&&strlen(line)){
char *p=dsearch(line,0,m);
if(p) printf("%s\n",p);
else printf("eh\n");
}
return 0;
}
最后,还请大家知道怎么用sort排结构体字符串的指教指教,感谢阅读!