「Day-2 提高笔记-字典树」

字典树

字典树是什么?

理论知识

  1. 插入操作

我们在插入的时候,先从根节点去向下遍历。对于字符串 \(S\) 的一位 \(S_i\)

  • 如果发现其在字典树中当前节点下有这个字符 \(S_i\),则继续向下,在向下的过程中每次给当前节点的次数加 \(1\),记录字符串前缀数量。
  • 若无这个字符,则开辟一个新的节点,记录节点编号,继续向下。
  1. 查询前缀数量

我们在查询前缀的时候,先从根节点去向下遍历。对于字符串 \(S\) 的一位 \(S_i\)

  • 若在字典树当前节点下没有 \(S_i\),则证明没有以该前缀为开头的单词。
  • 否则就继续向下,直到 \(S_n\),最后返回该节点数下的前缀数量。

时间复杂度

对于一个长度为 \(n\) 字符串。
\(Insert\)\(query\) 的时间复杂度都是 \(O(n)\)

代码实现

P8306 【模板】字典树

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 3e6 + 5;
int T;
int n,q;
char s[MAXN];
int ch[MAXN][100],cnt[MAXN],id = 0;

int got(char c){
	if(c >= 'A' && c <= 'Z') return c - 'A';
	if(c >= 'a' && c <= 'z') return c - 'a' + 26;
	return c - '0' + 52;
}

void Insert(char *s){
	int now = 0;
	for(int i = 0;s[i];i ++){
		int v = got(s[i]);
		if(!ch[now][v]){
			ch[now][v] = ++ id;
		}
		now = ch[now][v];
		cnt[now] ++; 
	}
	return;
}

int query(char *s){
	int now = 0;
	for(int i = 0;s[i];i ++){
		int v = got(s[i]);
		if(!ch[now][v]) return 0;
		now = ch[now][v];
	}
	return cnt[now];
}

void Clear(){
    memset(ch, 0, sizeof(ch[0]) * (id + 1));
    memset(cnt, 0, sizeof(cnt[0]) * (id + 1));
    id = 0;
}

signed main(){
	scanf("%d",&T);
	while(T --){
		scanf("%d%d",&n,&q);
		for(int i = 1;i <= n;i ++){
			scanf("%s",s);
			Insert(s);
		}
		for(int i = 1;i <= q;i ++){
			scanf("%s",s);
			cout << query(s) << '\n';
		}
		Clear();
	}
	return 0;
}

例题

P1481 魔族密码

这个题最开始我没想到怎么写,后来就发现,我们只要暴力 \(dfs\) 去跑一遍这个树即可,在遍历的过程中记录这条路径上的节点有多少个是单词的结尾。我们在 \(Insert\) 里面加一个判断节点 \(p\) 是否是单词结尾的 \(bool\) 变量即可。

#include<iostream>
using namespace std;

const int MAXN = 3 * 1e4 + 5;
int ans = 0;
int n,q;
char s[MAXN];
bool isend[MAXN];
int ch[MAXN][100],cnt[MAXN],id = 0;

void Insert(char *s){
	int now = 0;
	for(int i = 0;s[i];i ++){
		int v = s[i] - 'a'; 
		if(!ch[now][v]){
			ch[now][v] = ++ id;
		}
		now = ch[now][v];
		cnt[now] ++; 
	}
	isend[now] = 1;
	return;
}

void dfs(int p,int step){
	step += isend[p];
	bool back = 1;
	for(int i = 0;i < 26;i ++){
		if(ch[p][i] != 0){
			dfs(ch[p][i],step);
			back = 0;
		}
	}
	if(back == true){
		ans = max(ans,step);
	}
}

signed main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++){
		scanf("%s",s);
		Insert(s);
	}
	dfs(0,0);
	cout << ans << '\n';
	return 0;
}
posted @ 2024-10-16 22:35  To_Carpe_Diem  阅读(2)  评论(0编辑  收藏  举报