先聊一聊字典树是什么,他有什么用
来源于百度:
字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
简单来说,字典树就是将很多组字符串储存在一棵树上,并且可以快速的找到单词。
字典树的查找原理其实很像一个字典,,比如说你想要在英文字典中查找apple这个单词,你就需要先找到a这个字母,然后再去找p,再找…………,直到找到最后一个字母e,才能找到这个单词,而字典树也需要一层一层的进行储存。
下面先构建一棵简单的字典树:
如给出以下字符串“ab”,“bc”,“acd”,“bca”,“cab”,“caccd”,“cacca”,构建一棵Trie树。
建树如下图
图中红色的节点表示到此处已经构成一个单词了
字典树通过每个字母向下查找,能减少时间
在字典树上,每一条边对应一个字母,而每个节点表示的是这个节点的前缀,比如最左边的b连接的那个节点代表的前缀是{ab},因为{ab}与{acd}享有共同的前缀{a},所以他们享有共同的分支。
数组写法:
一、建树
void insert(){
int head=0,i;
for(i=0;s[i];i++){
int x=s[i]-'a';
if(trie[head][x]==0){
trie[head][x]=++tot;
}
head=trie[head][x];
}
}
二、查找
bool find(){
int head=0,i;
for(i=0;s[i];i++){
int x=s[i]-'a';
if(trie[head][x]==0) return false;
head=trie[head][x];
}
return true;
}
三、查找前缀数量
查找前缀数量需要把前两个代码块修改一下,具体在下面模板里
int find(){
int len=strlen(s);
int head=0;
int i,j,k;
for(i=0;i<len;i++){
int x=s[i]-'a';
if(!trie[head][x])return 0;
head=trie[head][x];
}
return sum[head];
}
完整模板(根据题目要求进行修改)
int trie[1005][26];
char s[100];
//bool over[1005]; 查询整个单词时候用到
int tot=0;
//int sum[1005]; 前缀数量
void insert(){ //创建字典树
int head=0,i;
for(i=0;s[i];i++){
int x=s[i]-'a';
if(trie[head][x]==0){
trie[head][x]=++tot;
}
head=trie[head][x];
// sum[head]++;//记录个数
}
// over[head]=true;
}
bool find(){ //查找单词
int head=0,i;
for(i=0;s[i];i++){
int x=s[i]-'a';
if(trie[head][x]==0) return false;
head=trie[head][x];
}
return true;
// return over[head];
}
//int find(){ //前缀数量时候用
// int len=strlen(s);
// int head=0;
// int i,j,k;
// for(i=0;i
// int x=s[i]-'a';
// if(!trie[head][x])return 0;
// head=trie[head][x];
// }
// return sum[head];
//}
链表写法:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char s[11];
int n, m;
bool p;
struct node{
int count;
node* next[26];
}*root;
node* build(){
node* k = new(node);
k->count = 0;
memset(k->next, 0, sizeof(k->next));
return k;
}
void insert(){
node* r = root;
for (int i = 0; s[i]; i++) {
int id = s[i] - 'a';
if (r->next[id] == NULL) r->next[id] = build();
r = r->next[id];
r->count++;
}
}
int search(){
node* r = root;
for (int i = 0; s[i]; i++) {
int id = s[i] - 'a';
r = r->next[id];
if (r == NULL) return 0;
}
return r->count;
}
int main(){
root = build();
scanf("%d", &n);
for (int i = 1; i <= n; i++){
cin >> s;
insert();
}
scanf("%d", &m);
for (int i = 1; i <= m; i++){
cin >> s;
printf("%d\n", search());
}
}
推荐使用数组写法
数组做法不管是在时间,空间,代码量上都要优于链表做法。
经典例题:HDU1251 统计难题
题目链接:点这里
AC代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int trie[1000005][26];
int tot = 0;
char s[105];
int sum[1000005] = { 0 };
void insert() {
int head = 0, i;
for (i = 0; s[i]; i++) {
int x = s[i] - 'a';
if (trie[head][x] == 0) {
trie[head][x] = ++tot;
}
head = trie[head][x];
sum[head]++;
}
}
int find(){
int head=0;
int i,j,k;
for(i=0;s[i];i++){
int x=s[i]-'a';
if(!trie[head][x])return 0;
head=trie[head][x];
}
return sum[head];
}
int main(){
while (cin.getline(s,105)) {
if (s[0] == NULL)
break;
insert();
}
while (cin.getline(s,105))
printf("%d\n", find());
return 0;
}
HDU 2846 Repository(Trie树)
题解:另一篇博客