字典树

[字典树](Trie Tree) 是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串)。
它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
——百度百科

字典树的时间复杂度和空间复杂度:
1、时间复杂度:插入和查询字符串的复杂度都是O(m),其中m是待插入或查询字符串的长度
2、空间复杂度:有公共前缀的字符串只需要村一次即可,节省了大量空间

基本性质

1、根结点不包含字符,除根结点以外的每个子结点都包含一个字符;
2、从根结点到某个结点,路径上经过字符串连接起来,为该结点对应的字符串;
3、每个结点的所有子结点包含的字符互不相同。

通常在实现的时候会在结点设置一个标志,标志该结点是否为字符串的末尾

字典树的经典应用

1.前缀匹配
我们来看这样一个问题:给出五个字符串,分别是abcd,abd,abc,efg,xyz,现在问在这五个字符串中有多少个字符串是abcdefg的前缀?
方法一:我们会想到暴力的方法,对字符串进行逐个匹配,找到abcdefg的前缀单词。但我们会发现当有n个字符串进行m次询问时,我们的时间复杂度为O(nk),k是字符串的平均长度,但这种操作的效率很低,显然无法让人接受。
方法二:使用字典树,只需要在对重复的字符记录一次(例如:字符串abcd和abd中,a,b字符都只需要记录依次)并对字符串的尾字符进行标记(如下图)。

graph TD root(( )) --> A1((a)) root --> E4((e)) E4 --> F4((f)) F4 --> G4((g_1)) A1 --> B1((b)) B1 --> C1((c_1)) B1 --> D2((d_1)) C1 --> D1((d_1)) root --> X5((x)) X5 --> Y5((y)) Y5 --> Z5((z_1))
#include <bits/stdc++.h>`
using namespace std;
const int maxn = 1000010;
int trie[maxn][26] = {0};//maxn等于总字符串的长度 
int color[maxn];//标记每串字符串的尾字符,依次来记录字符串出现的次数 
int k = 0;
/*
插入步骤:
1、记录这串字符串的长度len,并且命名一个变量p指向根
2、 在0~len中开始判断此字符串中的字符是否出现过,如果
没出现过,链接此字符,否则p将下移
3、最后记录这串字符串的尾字符,依次来记录这串字符串出现的次数 
*/ 
void insert(string str) 
{
	int len = str.length();
	int p = 0;
	for(int i = 0; i < len; i++)
	{
		int c = str[i] - 'a';
		if(!trie[p][c])
		{
			trie[p][c] = ++k;
		}
		p = trie[p][c];
	}
	color[p]++;
 } 

/*
查找步骤:
1、记录这串字符串的长度len,并且命名一个变量p指向根,同时命名一个变量用来记录前缀的总个数
2、在0~len中开始判断此字符是否被标记过,如果标记了则 k += color[p],
在判断此字符是否出现过,没有返回k,最后p下指
3、返回 k+color[p]
*/ 
int search(string str)
{
	int len = str.length();
	int p = 0;
	int k = 0;
	for(int i = 0; i < len; i++)
	{
		int c = str[i] - 'a';
		if(color[p]) k += color[p];
		if(!trie[p][c]) return k;
			p = trie[p][c];
	}
	return  k+color[p];
}
int main()
{
	int n, m;
	string str;
	cin >> n >> m;
	for(int i = 0; i < n; i++)
	{
		cin >> str;
		insert(str);
	}
	while(m--)
	{
		cin >> str;
		cout << search(str) << endl; 
	}
	return 0;
}
posted @ 2021-02-08 17:35  h星宇  阅读(180)  评论(0编辑  收藏  举报