您的位置:首页 > 其它

字典树(Trie树)总结-hdu1251

2017-05-01 16:20 155 查看

字典树(也称前缀树,Trie)

Trie树,又称单词查找树、字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

Trie高效的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提率的目的。

它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。

Trie树也有它的缺点,Trie树的内存消耗非常大。

三个基本特性:

1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符。  

2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 

3. 每个节点的所有子节点包含的字符都不相同。



应用:

1. 字符串检索,词频统计,搜索引擎的热门查询

2. 字符串最长公共前缀

Trie树利用多个字符串的公共前缀来节省存储空间,反之,当我们把大量字符串存储到一棵trie树上时,我们可以快速得到某些字符串的公共前缀。

举例:给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少.

解决方案:

首先对所有的串建立其对应的字母树。此时发现,对于两个串的最长公共前缀的长度即它们所在结点的公共祖先个数,于是,问题就转化为了离线 (Offline)的最近公共祖先(Least Common Ancestor,简称LCA)问题。

3. 排序:Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。

我以hdu1251为例,写一个Trie树的模板。

hdu1251

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1251

题意:老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀)。

这题有个小技巧。

如何判断空行:用gets()读入。读入的回车符会自动转换为NULL。所以循环读入,每次检测读入进来的字符串的第一个字符是否为NULL即可。

没什么题解,这题可以说是Trie树的模板题,直接Trie树解决就行了。

代码有两种,一种用链表实现,一种用数组实现,数组做法在时间和空间上都优于链表。(推荐用数组,链表理解简单些)

先看链表代码,简单理解一下,再看数组代码就轻松些。

链表代码

//当前代码并不能过,编译器从g++改为c++,交g++的话会超内存。
//原因好像是g++会优化,导致出现一些问题。感兴趣的自行百度。
//c++编译器不支持#include <bits/stdc++.h>,所以再把该头文件改为其它即过
//#include <bits/stdc++.h>包含所有头文件,你把它删掉写具体的头文件出来就OK
#include <bits/stdc++.h>
using namespace std;

struct Trie{
Trie *next[26];  //每个节点可以延伸26个子节点,即a-z
int num;         //含有当前前缀的单词个数
Trie()           //初始化
{
for(int i=0;i<26;i++) next[i]=NULL;
num=0;
}
}root;

int idx(char s)  //字母转数字
{
return s-'a';
}

void Trie_insert(char word[])  //插入单词
{
Trie *p=&root;
for(int i=0;word[i];i++)
{
//如果不存在当前前缀就新建
if(p->next[idx(word[i])]==NULL) p->next[idx(word[i])] = new Trie;
p = p->next[idx(word[i])];  //节点移到当前前缀
p->num++;  //含有当前前缀的单词+1
}
}

int Trie_search(char word[])  //查询存在以word[]为前缀的单词个数
{
Trie *p=&root;
for(int i=0;word[i];i++)  //找到前缀为word[]的节点
{
if(p->next[idx(word[i])]==NULL) return 0;
p = p->next[idx(word[i])];
}
return p->num;
}

int main()
{
char word[11];
while(cin.getline(word,11))
{
if(strlen(word)==0) break;
Trie_insert(word);
}
while(scanf("%s",word)!=EOF)
{
printf("%d\n",Trie_search(word));
}
return 0;
}


数组代码

//自行理解一下
#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+5;
int trie[maxn][26],num[maxn],cnt=1;

void Trie_insert(char word[])
{
int n=0;
for(int i=0;word[i];i++)
{
if(trie
[word[i]-'a']==0) trie
[word[i]-'a']=cnt++;
n = trie
[word[i]-'a'];
num
++;
}
}

int Trie_search(char word[])
{
int n=0;
for(int i=0;word[i];i++)
{
if(trie
[word[i]-'a']==0) return 0;
n = trie
[word[i]-'a'];
}
return num
;
}

int main()
{
char word[11];
while(gets(word))
{
if(word[0]==NULL) break;
Trie_insert(word);
}
while(scanf("%s",word)!=EOF)
{
printf("%d\n",Trie_search(word));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: