首页 > 其他分享 >Trie 一轮复习

Trie 一轮复习

时间:2022-09-18 08:44:38浏览次数:90  
标签:ch return 复习 Trie 一轮 int now 节点

字典树

字典树,顾名思义,就是一个像字典一样的树。
—— OI-wiki

普通 Trie

如图:

Trie 用边代表字母,那么从根节点到某个节点的路径表示一个字符串。

Trie 支持的操作有三个:

  1. 插入字符串
  2. 查询字符串是否存在
  3. 删除字符串

Trie 的储存

个人习惯用结构体来表示某种数据结构的节点:

struct Trie{
	int ch[26];
	bool end_,vis;
}T[inf];

题目的数据范围:\(n\le10^4,s\le50\)。

那么 Trie 中插入的点最多有 \(5\times10^5\)。

所以 inf=5e5+7

ch 表示子节点,共 26 个(根据题目不同子节点的的个数可能不同),end_ 表示这个点是不是字符串的结尾,vis 表示是不是第一次查找到这个点(某些题中有用到)。

插入

每找到一个节点,如果当前节点没有相应字母所对应的子节点,就新建一个节点作为其节点,直到整个字符串结束,标记一下 end_

没错就是线段树、平衡树那里的动态开点。

void insert(int now,int i)
{
	if(i==len){T[now].end_=1;return;}
	if(T[now].ch[a[i]]==0)
		T[now].ch[a[i]]=++cnt;
	insert(T[now].ch[a[i]],i+1);
}

当然也可以用迭代,但感觉迭代没有递归好理解。

(刚开始学的时候用的迭代,这次复习才弄递归)

void build(char *s)
{
	int now=1,i=0,len=strlen(s);
	while(i<len)
	{
		if(trie[now][a[i]]==0)
			trie[now][a[i]]=++cnt;
		now=trie[now][a[i]],i++;
	}
	end_[now]=1;
}

接下来的代码将不再展示迭代,因为远古时期的码风是在太丑了

查询

首先查找此字符串是否存在,如果按找路径找到某个节点为空则不存在。

再看最终节点是否为结束的节点,即有没有 end_ 标记。

最后在看是不是第一次查找到,即有没有 vis 标记。

#define OK 0
#define WRONG 1
#define REPEAT -1
int ask(int now,int i)
{
	if(now==0)return WRONG;
	if(i==len)
	{
		if(T[now].end_==0)return WRONG;
		if(T[now].vis==1)return REPEAT;
		T[now].vis=1;
		return OK;
	}
	return ask(T[now].ch[a[i]],i+1);
}

此处的 define 更不容易出错。

删除

首先说明,上题和大多数题中并没有此操作。

删除的时候分情况讨论:

  1. 当前节点不是叶子节点。

    清除标记即可。

  2. 当前节点为叶子节点,且根节点到当前节点有且仅有一个标记。

    删除整条路径就好。

  3. 当前节点为叶子节点,且根节点到当前节点不只有一个标记。

    从叶子节点删到上一个标记节点。

其实后两种情况差不多,都是从当前节点向上删。

bool pd_ye;
void remove(int now,int i)
{
	if(now==0)return;
	if(i==len)
	{
		pd_ye=1;
		for(int j=0;j<26;j++)
			if(T[now].ch[j])pd_ye=0;
		T[now].end_=0,T[now].vis=0;
		return;
	}
	remove(T[now].ch[a[i]],i+1);
	if(pd_ye&&T[T[now].ch[a[i]]].end_==0)
		T[now].ch[a[i]]=0;
	else pd_ye=0;
}

但这样操作可能会被卡空间:不断地插入删除,虽然 Trie 的规模很小,实际的数组花费很大。

和 Fhq_Treap 那里一样,可以弄一个 垃圾场,将删除的数存到垃圾场里,等在动态开点的时候直接去垃圾场里找点。

stack<int>bin;
void insert(int now,int i)
{
	if(i==len){T[now].end_=1;return;}
	if(T[now].ch[a[i]]==0)
	{
		if(bin.empty())T[now].ch[a[i]]=++cnt;
		else T[now].ch[a[i]]=bin.top(),bin.pop();
	}
	insert(T[now].ch[a[i]],i+1);
}
bool pd_ye;
void remove(int now,int i)
{
	if(now==0)return;
	if(i==len)
	{
		pd_ye=1;
		for(int j=0;j<26;j++)
			if(T[now].ch[j])pd_ye=0;
		T[now].end_=0,T[now].vis=0;
		return;
	}
	remove(T[now].ch[a[i]],i+1);
	if(pd_ye&&T[T[now].ch[a[i]]].end_==0)
		bin.push(T[now].ch[a[i]]),T[now].ch[a[i]]=0;
	else pd_ye=0;
}

应该是对的,但我不是很确定(因为没找到例题),欢迎 dalao hack 和提供正确的代码。

通过这三个操作,就可以在较短的时间里实现插入、查找、删除一个字符串等操作。

01Trie

普通的 Trie 是一种 26 叉树,实际上更常用的是 2 叉树,也就是 01Trie。

(咕咕咕)

可持久化

(不会,待学)

标签:ch,return,复习,Trie,一轮,int,now,节点
From: https://www.cnblogs.com/Zvelig1205/p/16703063.html

相关文章

  • c语言数据结构复习
    c语言数据结构复习第1章:基本概念第2章:线性结构2.1--线性表及其实现2.1.1-引子:多项式及其表示法1:顺序存储直接表示多项式法2:用顺序存储结构表示多项式说明:以上例......
  • Java8Stream流复习和api总结
    构建方式list.stream();Stream.of(list);基础常用APIStream<Number>stream=list.stream();//获取最大值stream.max(比较器);//获取最小值stream.min(比较器);......
  • java复习随笔(十四)——类加载器、反射
    类加载器类加载当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外状况,JVM将会连续完......
  • java复习随笔(十三)——Stream流
    Stream流的生成方式Stream流的使用生成流通过数据源(集合,数组)生成流list.stream()中间操作一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的......
  • 复习python基础
    ......
  • java复习随笔(八)——线程(二)——生产者和消费者
    生产者消费者生产者消费者模式概述生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻所谓生产者消费者问题......
  • NOIP复习(一)最短路
    dijkstra复习一遍模板。Dijkstra模板适用性:适用于非负权图。每个点第一次从堆中被取出时,其\(dis\)一定是最短路。\(Dijkstra\)贪心的正确性:现证明:取出\(x\)时,它......
  • NOIP复习(二)二分算法
    提供一种二分写法,不太用考虑边界的问题。intl=st,r=ed,ans=ed+1;while(l<=r){intmid=(l+r)>>1;if(check(mid))ans=mid,l=mid+......
  • mysql复习
    ##数据库的好处 1.持久化数据到本地 2.可以实现结构化查询,方便管理##数据库相关概念 1、DB:数据库,保存一组有组织的数据的容器 2、DBMS:数据库管理系统,又称为数据库软件(产......
  • java复习随笔(四)——集合
    集合Collection单列Collection单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素。JDK不提供此接口的任何实现,它提供更具体的子接口(如set和list)的实现。......