首页 > 其他分享 >树Hash的一些常用姿势

树Hash的一些常用姿势

时间:2023-03-04 10:23:01浏览次数:47  
标签:姿势 常用 Hash 同构 son 算法 vector 序列号

Preface

树Hash,是一种常用的用于判断树是否同构的算法

所谓两棵树同构,即通过给其中一棵树重新标号后可以和另一棵树完全相同

一般我们在考虑同构的时候都是考虑的有根树,以下也以有根树为例

当然无根树的同构判断也很简单,分别找出两棵树的重心(至多两个),以重心为根跑有根树的树Hash然后看结果是否一致即可

之前网上常见的树Hash的算法都是有概率被卡的,即如果两棵树真的同构则它们的Hash值一定相同,但如果两棵树不同构也有可能得到相同的Hash值

但最近看到一种很牛逼的不会出错的算法——AHU算法,复杂度多个\(\log\),但正确性得到了保证

关于各种方法的取舍,其实就是乱草能不被卡就行,甚至你可以自己发明,同时把多种方法一起使用也是可以的


基于和的Hash

先上个公式:

\[f_u=size_u\times \sum f_{son(u,i)}\times seed^{i-1} \]

其中\(f_u\)表示以\(u\)为根的子树对应的Hash值,特殊地,叶节点的Hash值设为\(1\)

\(son(u,i)\)表示\(u\)的所有儿子以\(f\)为关键字排序后,排名第\(i\)的儿子,\(seed\)就是一个初始选定的种子

然后在跑的时候用unsigned long long自然溢出取模即可

当然这种方法反例比较多,比如下面的

可以算出两棵树的Hash值都是\(60(1+seed)\)


基于异或的Hash

还是先上公式:

\[f_u=\bigoplus f_{son(u,i)}\times seed+size_{son(u,i)} \]

里面出现的量和意义和上面类似就不再赘述了,注意\(son_u\)无需再排序

由于异或的性质,如果一个节点下有多棵本质相同的子树,这种哈希值将无法分辨该种子树出现\(1,3,5,\cdots\)次的情况


基于质数作参的Hash

公式如下:

\[f_u=1+\sum f_{son(u,i)}\times prime_{size_{son(u,i)}} \]

而这里的\(son_u\)也无需排序,同时\(prime_t\)表示第\(t\)个质数

这种做法由于引入了多个参数,其实已经比较难卡了,具体反例我也找不出的说


和与异或混用+shift操作

在实际应用中一般会优先考虑这种方法,兼顾了写代码的简洁度和正确性

我们可以把原来对\(seed\)作乘法的算法变成一次shift,根据某些玄学原理这样可以很大概率地避免冲突

关于shift的代码具体如下:

inline u64 shift(u64 x)
{
	x^=x<<13; x^=x>>7; x^=x<<17; return x;
}

因此我们每次把儿子的Hash值往上合并一层的时候进行一次shift,同时同时维护和与异或的信息即可

大概的做法就如上所述,写法可以看这道题的代码


AHU算法

我们考虑给每个点一个序列号,使得所有同构的子树的根节点的序列号相同

考虑以下这种保证绝对正确率的方法:对于一个点\(u\),我们把它的所有儿子的序列号扔进一个vector里,然后把vector排序

不难发现如果我们用这个vector作为序列号的话,是显然不会误判的

但用vector来当序列号可能会出现各种vector的嵌套情况,未免有点太蠢了,因此我们用map把每个vector映射成一个标号即可

乍一看感觉复杂度很爆炸,但由于STL的优秀实现机制(map是用红黑树实现的),因此map <vector<int>,int>插入一个vector的复杂度不会超过这个vector的长度乘\(\log\)

由于每个点只会在map里查找一次,因此总复杂度就是\(O(n\log n)\)的

当然这种算法在遇到无根树的情况会比较麻烦,当有两个重心时需要在两个重心之间的那条边上建一个虚点作根

当然虚点的vector需要特殊处理,往它的vector里塞个\(-1\),来把它和普通的点区分开

大概的做法就如上所述,写法可以看这道题的代码


Postscript

学了这么多年我才发现连这么一个简单算法都不会,还是太菜太菜

标签:姿势,常用,Hash,同构,son,算法,vector,序列号
From: https://www.cnblogs.com/cjjsb/p/17177725.html

相关文章

  • datetime time 常用操作
    1.获取当前时间的两种方法importdatetime,timenow=time.strftime("%Y-%m-%d%H:%M:%S")printnownow=datetime.datetime.now()printnow2.获取当前时间前一小时、前一......
  • 常用的基本Dos命令
    Windows+R:输入cmd,运行控制台#盘符切换:盘名+:#查看当前目录下的所有文件:dir#切换目录:cd+文件名#返回上一级:cd..#清理屏幕:cls#退出终端:exit#查看电脑......
  • Docker CLI docker buildx build 常用命令
    Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化。Doc......
  • 常用Linux shell命令汇总,总有一条你需要
    对于经常使用linux系统的博主来说,基本常用的shell命令是少不了的,总结一下,哪天不会了,可以来翻看下O(∩_∩)O。对你有用记得收藏哦!欢迎补充!1.检查远程端口是否对bash开放:......
  • 常用工具类
    Bean转换工具类importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.BeanUtils;importjava.util.List;importjava.util.Set;importjava.util.str......
  • 测试框架的(概述、构成以及常用框架类型)
    测试框架大致可以分类四大类模块:(基础模块)、(管理模块)、(运行模块)、(统计模块)基础模块:1.底层核心驱动库:用户操作程序的第三方库,例如selenium2.可重用的组件:可重......
  • kafka 常用命令
    kafka介绍kafka官网:http://kafka.apache.org/各个名词解释:*producer:发布消息的对象称之为消息生产者(Kafkatopicproducer)*topic:Kafka将消息分门别类,每一类的消息称......
  • box-sizing常用的属性有哪些?分别有什么作用?
    (1)标准盒模型box-sizing:content-box;作用:宽度和高度分别应用到元素的内容框。在宽度和高度之外绘制元素的内边距和边框(元素默认效果)。(2)IE盒模型box-sizing:border-b......
  • zookeeper 常用命令
    ###Zookeeper服务常用命令```shell启动ZooKeeper服务:./zkServer.shstart查看ZooKeeper服务状态:./zkServer.shstatus停止ZooKeeper服务:./zkServer.shstop重启Z......
  • IDEA常用快捷键总结
    IDEA常用快捷键总结前言IDEA中提供了很多快捷键,点击File-->Settings-->keymap便可进入看到IDEA提供的快捷键。我们也可以搜索和自定义所有快捷键,下面给出的是IDE......