学习NOTE 7——字典树(Trie)

之前忘写了。。。赶紧补一篇。我是煞笔。

是什么

字典树(trie),可以理解为把字典建成树。 为什么这么说?(我哪知道)
如下:
appaddbye

          (1)
          / \
         a   b
        / \   \
       p   d   y
      /     \   \
     p       d   e

欸,你别说,你还真别说,果然是“字典树”。

那字典树是用于干什么的?
给一下定义:字典树是一种用于高效存储和检索字符串集合的树形结构。
所以你知道了吧,就是用于储存与查找字符串的工具。

怎么实现

既然是“字典树”,那首先肯定要建树啊,就像很多树上的算法一样,都是树的嘛。

注意观察“是什么”里的例子,树是怎么建的。

  • 用一个节点表示一个字符。
  • 将前缀一样的字符串的前缀部分并在一起,剩下的部分作为子树。怪不得字典树也叫“前缀树”

那么我们需要记录什么?
眼睛向下看↓

// 定义常量:N为最大节点数(1e6+10),M为字符集大小(26个小写/大写字母)
const int N=1e6+10,M=26;

//trie[N][M]:字典树(Trie)的存储数组,trie[p][c]表示从节点p通过字符c到达的子节点编号
//cnt[p]:记录以节点p结尾的单词个数(即该字符串被插入的次数)
//tot:节点总数,用于给新节点分配编号(根节点编号为0)
int trie[N][M],cnt[N],tot;

这样就记住了节点、字符串、序号。

那建树...

//插入单词到Trie树
void insert(string s){
	int p=0;//从根节点开始
	for(auto c:s){
		int ch=c-'a';
		if(!trie[p][ch]) trie[p][ch]=++tot;//若该分支不存在则新建节点
		p=trie[p][ch];//沿分支向下走
	}
	cnt[p]++;//单词末尾节点计数+1,表示该单词存在且插入次数
}

有了树,别忘了我们要干什么 (别忘本),回顾一下定义:字典树是一种用于高效存储和检索字符串集合的树形结构。

储存了,就该检索了。那又怎么查呢?

根据字典树的特性,我们目前可以查这些东西:查询单词s是否在Trie树中(查单词)、是否存在以s为前缀的单词(查前缀)。
转化一下:要查的字符串是否和Trie树的一个链组成的字符串、是否存在一个链的一部分和要查的单词/前缀相等。

查询的方式和建树是一样的,那么这就很好写了。

查单词:

//查询单词s是否在Trie树中
bool search(string s){
	int p=0;
	for(auto c:s){
		int ch=c-'a';
		if(!trie[p][ch]) return 0;//路径中断,单词不存在
		p=trie[p][ch];
	}
	return cnt[p]>0;//返回该单词是否被插入过(cnt[p]>0表示存在)
}

查前缀:

//判断是否存在以s为前缀的单词
bool startwith(string s){
	int p=0;
	for(auto c:s){
		int ch=c-'a';
		if(!trie[p][ch]) return 0;//路径中断,无此前缀
		p=trie[p][ch]; 
	}
	return 1;//能走完路径,说明有单词以s为前缀
}

这些就是字典树的基本功能啦~


栗子:

PID:P2580

Ber,为什么不能直接不让他玩炉石?

思路:版字题,考虑先把所有人的名字建一个字典树,每次查找时用map统计一下次数就ok了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
//Trie树节点数组:trie[p][c]表示节点p通过字符c(0-25)到达的子节点编号,0表示不存在
const int N=1e6+10,M=26;
int trie[N][M],cnt[N],tot;
map<string,int> mp;
//插入单词s到Trie树
void insert(string s){
	int p=0;//从根节点开始
	for(auto c:s){
		int ch=c-'a';
		if(!trie[p][ch]) trie[p][ch]=++tot;//若该分支不存在则新建节点
		p=trie[p][ch];//沿分支向下走
	}
	cnt[p]++;//单词末尾节点计数+1,表示该单词存在且插入次数
}
//查询单词s是否在Trie树中
bool search(string s){
	int p=0;
	for(auto c:s){
		int ch=c-'a';
		if(!trie[p][ch]) return 0;//路径中断,单词不存在
		p=trie[p][ch];
	}
	mp[s]++;//记录该单词被查询的次数
	return cnt[p]>0;//返回该单词是否被插入过(cnt[p]>0表示存在)
}
int n,q;
string s;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0); 
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>s;
		insert(s);//添加名字
	}
	cin>>q;
	while(q--){
		cin>>s;
		bool x=search(s);
		if(!x) cout<<"WRONG\n";//名字不存在
		else if(mp[s]>1) cout<<"REPEAT\n";//名字存在但重复查询
		else cout<<"OK\n";//名字存在且首次查询
	}
	return 0;
}
posted @ 2026-08-19 21:01  _T_M_T  阅读(1)  评论(0)    收藏  举报