博客通过学习这篇博客(https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html)+自己的理解写的
字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
字典树其实主要就是查询,当你查询的时候,你其实是不知道你最开始是插入的是什么,只是通过特定的路径去判断这个字符串有没有出现过。所以一般由来查找字符串有没有出现过或者出现的次数。
什么时候用比较好,比如有n次输入m次查询,你就不能一次又一次的遍历,时间复杂度太高了,所以需要字典树,字典树的时间复杂度是O(m * n)。
如何存储字符串呢?字典树储在一个树上,比如只含有26个字母的字符串,从跟开始每个节点最多有26个子节点。用树的枝干来存储数据。如图:
可以顺着枝干,找到cat,cash,app,apple,aply等,这就是构建字典树的过程。
如何建树?代码如图所示:
void Insert(char *s, int rt){
len = strlen(s);
for(int i = 0; i < len; i++){
int id = s[i] - 'a';
if(!trie[rt][id]){
trie[rt][id] = ++ tot;
}
rt = trie[rt][id];
}
}
那么查询呢?
bool Find(char *s, int rt){
len = strlen(s);
int cn = 0;
for(int i = 0; i < len; i++){
int id = s[i] -'a';
if(trie[rt][id] != 0)cn++;
rt = trie[rt][id];
}
if(cn = len)return true;
else return false;
}
例题:
统计难题 HDU 1251
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1251
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 2000005
int trie[maxn][26];
int sum[maxn], len, tot;
char s[12];
void Insert(char *s, int rt){
len = strlen(s);
for(int i = 0; i < len; i++){
int x = s[i] - 'a';
if(!trie[rt][x]){
trie[rt][x] = ++tot;
}
sum[trie[rt][x]]++;
rt = trie[rt][x];
}
}
int Find(char *s, int rt){
len = strlen(s);
for(int i = 0; i < len; i++ ){
int x = s[i] - 'a';
if(trie[rt][x] == 0)return 0;
rt = trie[rt][x];
}
return sum[rt];
}
int main(){
int rt = 0;
tot = 0;
while(cin.getline(s,12)){ //输入单词
if(strlen(s)==0 || s[0]==' ') //如果读入字符串的长度为0或者是空格,说明读入的是空行
break;
Insert(s, rt);
}
while(scanf("%s",s)!=EOF){
printf("%d\n",Find(s, rt)); //返回s为前缀的单词的数量
}
return 0;
}
Phone List HDU 1671
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1671
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
#define N 111111
int trie[N][11];
int vis[N];
char s[15];
int n, T, tot, rt, flag;
int Insert(char *s)
{
int len = strlen(s);
rt = 0;
int ans = 0;
for(int i = 0; i < len; i++)
{
int x = s[i] - '0';
if(!trie[rt][x]){
memset(trie[tot], 0, sizeof(trie[tot]));
trie[rt][x] = tot++;
ans ++;
}
else if(vis[trie[rt][x]])flag = 1;
rt = trie[rt][x];
}
if(!ans)flag = 1;
vis[rt]++;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
tot = 1;
flag = 0;
memset(trie[0], 0, sizeof(trie[0]));
memset(vis, 0, sizeof(vis));
while(n--)
{
scanf("%s", s);
if(!flag)
{
Insert(s);
}
}
if(flag)printf("NO\n");
else printf("YES\n");
}
}
单词数 HDU 2072
链接:http://acm.hdu.edu.cn/showproblem.php?pid=2072
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include <iostream>
#include <cstdio>
#include <sstream>
#include <set>
using namespace std;
#define N 100000
int trie[N][26];
int vis[N];
int tot, rt, len, ans;
void Insert(string s)
{
rt = 0;
len = s.size();
for(int i = 0; i < len; i++)
{
int x = s[i] - 'a';
if(!trie[rt][x])
{
memset(trie[tot], 0, sizeof(trie[tot]));
trie[rt][x] = tot++;
}
rt = trie[rt][x];
}
if(!vis[rt]&& rt != 0)
{
ans ++;
}
vis[rt] = 1;
}
int main()
{
string s;
memset(vis, 0, sizeof(vis));
memset(trie[0], 0, sizeof(trie[0]));
tot = 1;
ans = 0;
while(getline(cin, s) && s != "#")
{
istringstream sin(s);
string w;
while(sin >> w)
Insert(w);
}
cout << ans << endl;
return 0;
}
题目:前缀统计
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int trie[maxn][26];
int sum[maxn], tot = 0;
char str[maxn];
void Insert(char *s, int rt)
{
int len = strlen(s);
for(int i = 0; i < len; i++)
{
int x = s[i] - 'a';
if(!trie[rt][x])
{
trie[rt][x] = ++tot;
}
rt = trie[rt][x];
}
sum[rt]++;
}
int query(char *s, int rt)
{
int len = strlen(s);
int ans = 0;
for(int i = 0; i < len; i++)
{
int x = s[i] - 'a';
if(!trie[rt][x])return ans;
if(trie[rt][x]) ans += sum[trie[rt][x]];
rt = trie[rt][x];
}
return ans;
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
while(n--)
{
scanf("%s", str);
Insert(str, 0);
}
while(m--)
{
scanf("%s", str);
printf("%d\n",query(str, 0));
}
return 0;
}