字典树(数组实现)理解

字典,一般是我们用来查找的工具,就拿牛津英语词典来说,它里面包含了数量庞大的单词,如果我们漫无目的的去找一个词,仿佛大海捞针.但是词典给每个单词进行了排序,排序的规则也很简单,按照单词每位字母的字母表顺序排列,相同的就按照下一位的顺序排.只要按照这个规则,我们想查找到一个单词也就很容易了.而字典树之所以被叫做字典树,就是因为它有着类似于字典的结构,方便了字符串的前缀匹配等问题.

字典树,顾名思义,就是一个树形结构,举个例子,假设有四个单词:a,abcd,acd,abc;建树过程如下:

 第一个词为a,我们从根节点出发,没有发现存在有和它的第一个字母(也就是它本身)的节点相同的节点,所以我们新建一个节点,来记录这个节点,并且因为该单词已经遍历完了,所以记录一下这个节点;然后到了第二个单词abcd,遍历该单词的每一位字母,第一个字母为a,此时从根节点出发,会发现存在a节点,所以就向a方向前进,然后到了字母b,但是树的a节点没有后续节点了,所以我们在a后面建立一个新节点,以此类推知道第二个单词记录完.第三个单词如上操作,但是第一个单词匹配走到树节点a后,没有新的节点了,就建立新节点.最后就是abc,这时会发现从根出发,它的每个字母都可以和树上的节点匹配,那么就记录一下就行了.

关于标记有两种标记,主要看你的题目是怎样要求的,如果是只要求匹配字符串前缀(例如ab就是abc前缀,a也是abc前缀,abc也是abc前缀)出现的次数那么每次到达一个节点都要记录一次.如果只要求统计单词出现次数,那么就只用记录每次单词的最后一位出现的位置;

看到这种结构其实是可以用链表实现,每个链表阶段存一个长度为26的指针数组(依情况而定,记录数字就开长度为10的),表示匹配的字符串的字母.还包含一个val值记录匹配到该节点的次数.但是链表实现的代码有点长,我们可以尝试用数组去存,这样可以减少敲代码的时间(有板子直接用就不用了考虑啦qwq).

举个例子来代码实现:


https://acm.hdu.edu.cn/showproblem.php?pid=1251

Problem Description

Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).

Input

输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.

Output

对于每个提问,给出以该字符串为前缀的单词的数量.

Sample Input

banana 

band 

bee

absolute

acm 

ba 

band

abc

Sample Output

2 3 1 0


 这个题就是妥妥的字典树模板题,开个数组维护每个前缀出现的次数就行了.先上代码:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int tire[1000005][30];//将节点拆分成二维数组
int sum[1000005];//记录每种前缀出现的次数
int tot=0;
//tot来记录每种前缀,例如存abc这个单词,tot=1时表示a,等于2时表示ab,等于3时表示abc
//不同种类都有对应的tot
char s[12];//表示输入的字符串
void add(string str)
{
	int root=0,len=str.size();
	for(int i=0;i<len;i++)
	{
		int x=str[i]-'a';//找到建树的字符位置,记录
		if(!tire[root][x])//如果之前存了这个节点就不要再建了
			tire[root][x]=++tot;
		root=tire[root][x];//节点往下遍历
		sum[root]++;//记录次数增加,记录前缀,只记录单词就把这行移到括号外
	}	
	return;
}
int find(string str)
{
	int ans=0;
	int root=0,len=str.size();
	for(int i=0;i<len;i++)
	{
		int x=str[i]-'a';
		if(tire[root][x]==0)return 0;
		root=tire[root][x];
	}
		ans=sum[root];
	return ans;
}
int main()
{
	string str;
	char ch;
	while((ch=getchar())!='\n')
	{
		str="";
		s[0]=ch;
		int i=1;
		while((ch=getchar())!='\n')
		{
			s[i]=ch;
			i++;
		}
		for(int j=0;j<i;j++)str+=s[j];
		add(str);
	}
	while(cin>>str)
	{
		cout<<find(str)<<endl;
	}
	return 0;
}

这个题用二维数组写,实现的过程类似于把树状结构拆分开,集合码成一个顺序表,它们之间靠tot连接.

以上就是我初学字典树的笔记.如有不足,希望大佬们不吝赐教.蒟蒻洗耳恭听.qwq

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值