Trie树(字典树)讲解

Trie树

先聊一聊字典树是什么,他有什么用

来源于百度:
字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

简单来说,字典树就是将很多组字符串储存在一棵树上,并且可以快速的找到单词。

字典树的查找原理其实很像一个字典,,比如说你想要在英文字典中查找apple这个单词,你就需要先找到a这个字母,然后再去找p,再找…………,直到找到最后一个字母e,才能找到这个单词,而字典树也需要一层一层的进行储存。

下面先构建一棵简单的字典树:

如给出以下字符串“ab”,“bc”,“acd”,“bca”,“cab”,“caccd”,“cacca”,构建一棵Trie树。

建树如下图
Trie树(字典树)讲解_第1张图片
图中红色的节点表示到此处已经构成一个单词了
字典树通过每个字母向下查找,能减少时间

在字典树上,每一条边对应一个字母,而每个节点表示的是这个节点的前缀,比如最左边的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树)
题解:另一篇博客

你可能感兴趣的:(字符串,trie,字典树,ACM,字符串)