Trie树可以被用来统计公共前缀,例如模板题的HDU统计难题。
以及模板题的一个变型题
POJ Shortest Prefixes
这道变型题用来找出每个单词最短的前缀缩写,也就是只要找出从第一个字母一直到最后一个字母哪一个前缀组合在所给样例中只存在一次。里附上一个利用Trie树比网上例程更简单的做法,也就是在建立完Trie树,到达每个结点后,先直接输出,再判断这个结点被几个单词所共享。如果只有一个,就可以直接return,再判断下一个单词;如果这个单词走到了头也没有出现特有的一个字母位也没有关系,因为已经把整个单词输出完了,而其他更长的单词也会因为前面的字母结点都已经共享,而输出相应更长的前缀。
附AC代码:(抓狂,WA了四次的原因居然是字符串开的不够大,比对了网上的AC代码发现要开到1000*25才可以)
#include<iostream>
using namespace std;
#include<stdio.h>
#include<string.h>
struct node
{
node* br[26];
int sum ;
node()
{
sum =0;
memset(br,0,sizeof(br));
}
} ;
node *head;
char list[1001][25];
void insert(char *m)
{
int l = strlen(m);
node *t = head;
node *s;
for(int i = 0; i < l;i++)
{
int x= m[i]-'a';
if(t->br[x]==NULL)
{
s = new node;
s->sum = 1;
t->br[x]= s;
}
else
{
t->br[x]->sum++;
}
t = t->br[x];
}
}
void find(char * m)
{
int l = strlen(m);
node *h= head;
for(int i = 0 ;i< l;i++)
{
int x= m[i]-'a';
if(h->sum!=1)
cout<<m[i];
else {
return;
}
h = h->br[x];
}
}
int main()
{
head = new node;
int i,t=0;
while(~scanf("%s",list[t]))
insert(list[t++]);
for(i=0;i<t;i++)
{
if(i)cout<<endl;
cout<<list[i]<<' ';
find(list[i]);
}
return 0;
}
其次就是Trie树最核心的用法了,那就是作为字典存储相应信息。作为字典存储时,每个结点的数据域应该改变,例如可以删去当前结点共用的单词数,增添一个变量判断从头节点到当前结点能否构成一个完整的单词,而当可以的时候,还要记录下另一个单词,因此每个结点还应附加一个字符串数组用来储存。
HDU What Are You Talking About
本题给出了英语和火星语的对照,再给出若干句火星文,需要我们翻译成英文,这道题其实很简单,但是在输入操作的时候有一些小技巧,阻挡了AC的步伐
附AC代码如下:
#include<iostream>
#include<cstring>
using namespace std;
typedef struct Trie_node
{
int count; // 统计单词前缀出现的次数
struct Trie_node* next[26]; // 指向各个子树的指针
bool exist; // 标记该结点处是否构成单词
char trans[11]; // 翻译
}TrieNode , *Trie;
TrieNode* createTrieNode()
{
TrieNode* node = (TrieNode *)malloc(sizeof(TrieNode));
node->count = 0;
node->exist = false;
memset(node->next , 0 , sizeof(node->next)); // 初始化为空指针
return node;
}
void Trie_insert(Trie root, char* word , char* trans)
{
Trie node = root;
char *p = word;
int id;
while( *p )
{
id = *p - 'a';
if(node->next[id] == NULL)
{
node->next[id] = createTrieNode();
}
node = node->next[id]; // 每插入一步,相当于有一个新串经过,指针向下移动
++p;
node->count += 1; // 这行代码用于统计每个单词前缀出现的次数(也包括统计每个单词出现的次数)
}
node->exist = true; // 单词结束的地方标记此处可以构成一个单词
strcpy(node->trans , trans);
}
char* Trie_search(Trie root, char* word)
{
Trie node = root;
char *p = word;
int id;
while( *p )
{
id = *p - 'a';
node = node->next[id];
++p;
if(node == NULL)
return 0;
}
if(node->exist) // 查找成功
return node->trans;
else // 查找失败
return NULL;
}
int main(void)
{
Trie root = createTrieNode(); // 初始化字典树的根节点
char str1[3003] , str2[3003] , str[3003] , *p;
int i , k;
scanf("%s",str1);
while(scanf("%s",str1) && strcmp(str1 , "END") != 0)
{
scanf("%s",str2);
Trie_insert(root , str2 , str1);
}
getchar();
gets(str1);
k = 0;
while(gets(str1))
{
if(strcmp(str1 , "END") == 0)
break;
for(i = 0 ; str1[i] != '\0' ; ++i)
{
if(str1[i] >= 'a' && str1[i] <= 'z')
{
str[k++] = str1[i];
}
else
{
str[k] = '\0';
p = Trie_search(root , str);
if(p)
printf("%s", p);
else
printf("%s",`
str);
k = 0;
printf("%c", str1[i]);
}
}
printf("\n");
}
return 0;
}
最后来道题练下手吧(05/24)
Babefish
附AC代码
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
struct node{
node*br[26];
char x;
bool exist;
char trans[12];
node()
{
for(int i = 0;i<26;i++)
br[i]=NULL;
}
};
node*head;
void insert(char *text,char *trans)
{
int l = strlen(text);
node *h=head,*s;
for(int i = 0; i < l; i ++)
{
int x = text[i]-'a';
if(h->br[x]==NULL)
{
s = new node();
h->br[x] = s;
h->exist= true;
}
h=h->br[x];
}
strcpy(h->trans,trans);
}
void find(char *text)
{
int l = strlen(text);
node *h = head;
int valid = 1;
for(int i = 0; i< l;i++)
{
int x = text[i]-'a';
h = h->br[x];
if(h ==NULL)
{
valid = 0;
break;
}
}
if(valid&&h->exist)
valid = 1;
else
valid = 0;
if(valid )
printf("%s\n",h->trans);
else
printf("eh\n");
}
int main()
{
head = new node;
char str1[15], str2[15], str[30];
int i,j;
while(gets(str))
{
if(strlen(str) == 0)
break;
i = 0; j = 0;
while(str[i] != ' ')
str1[j++] = str[i++];
str1[j] = '\0'; i++;
j = 0;
while(str[i] != '\0')
str2[j++] = str[i++];
str2[j] = '\0';
insert(str2, str1);
}
while(~scanf("%s",str))
find(str);
return 0;
}