如何动态的向数组中插入键值对_OI/ACM中的哈希表,一些哈希算法以及题目

597b866c799fb60cf3fd6dafaece7555.png

<如果你已经学习过哈希有关知识,也推荐看一下后面的补充知识及题目>

问题引出:假设有一个集合

,里面装满了形如
的键值对,
值是唯一的。现在想在尽可能短的时间内取出
对应的
的值,要怎么做比较好呢?

假设集合中有

个不同的
,那么当我们可以定义一个函数以
为自变量的函数
,它能使得
等于某个
的整数,并且对于
。当计算
函数的代价是
的,通过开一个大小为
类型的数组就可以取出对应的值了,时间复杂度和计算
函数的代价也是相同的。

示例代码如下:(领会精神QAQ)

Value 

就被称作
哈希函数,如果能构造出这个函数,就可以满足要求。

假设

是一个字符串类型,那么定义
=
就可以构造出这个函数。但怎么保证对于
呢?答案
没有必要保证

假设两个

的函数值相同,那把"
的键值对"其中一个放到
,一个放到
的位置上,不就可以了吗?
(Linear Probe Hashing)

举个例子:(课件来自CMU15-445,Hash)

7f1d4ba1f52e613ed4e0942f7004b8a9.png
向哈希表中插入key=A的val

e0ec38adcb5f131efd64a55fba7b5bc0.png
向哈希表中插入key=B的键值对

923bae00cd8126bf2922f154666b0af6.png
插入key=C的键值对,但F(C)的位置和F(A)的位置冲突,那么会一直向下找到第一个可以放的位置为止,在那个位置插入C|val的键值对。

这样问题就解决了。在查询一个

所对应的
值的时候,我们最多经历"与
的相同的值的个数"(即哈希值的
碰撞次数)次查找,就能找到所对应的值。

事实上哈希函数是自己定义的……所以只要计算起来比较方便,碰撞次数又比较少就可以啦。

示例代码如下(领会精神QAQ):

pair

上面的思路仅能容纳

个元素,换一种角度:如果把hash_map开成
个初始为空的
链表,对于
相同的键值对共享一个链表,这样就可以容纳无穷无尽的元素了,甚至可以维护动态变化的集合,因为链表是支持插入和删除的。(关于链表的文章以后会提到)
List<pair<Key, Value>> hash_map[N];
int F(Key x){ ... } //details omitted.
void init(){
    for(Key x, Value y : S)
    {
        int hash_position = F(x);
        hash_map[hash_position].insert( {x, y} );
    }
}
void Insert_into_hashmap(Key x, Value y)
{
    int hash_position = F(x);
    hash_map[hash_position].insert( {x, y} );
}
Value getvalue(Key x) {
    return bullet[F(x)].second;
}

哈希函数所想到的:(与哈希表无关)

题目:给一个字符串

次询问,每次询问给出一个字符串
,求
在字符串
中的出现次数。(
,字符串只由
组成)

考虑构造一个哈希函数

,其构造方法如下:

(领会精神……QAQ)

int 

通过自然溢出,把一个string类型的值经过

的复杂度映射为
类型,如果两个string的哈希函数值相同,那么可以认为这两个string也是完全相同的。

但由于哈希函数存在碰撞,参考字符串哈希的方法,可以采取双哈希函数的做法,只有两个函数的值都是相同的时候,才认为两个字符串是相同的。

那么做法也就呼之欲出了:首先计算

中所有子串的哈希函数值,并将其插入到
(红黑树)中,对于每次询问的字符串
,计算
的哈希函数值
,并查找
中出现了多少次,出现的次数便是答案。复杂度为
(红黑树查询是
级别的,但在OI/ACM比赛中可以近似看成常数)。代码复杂度低,容易编写。

补充知识:

<为了不让已经会hash表的童鞋直接跑路,强行去查了查这是啥>

<似乎国内的没啥>

ROBIN HOOD HASHING:基于(Linear Probe Hashing)的改进算法。

由于在最差情况下,Linear Probe Hashing的策略取出一个

的代价很高。

比如

,且按顺序插入
,那么它们平均要寻址的次数分别对应了1,2,3,4,5,占据了关于
的位置。

这似乎没什么,但这样会使得之后能映射到

这些位置的键值对
走更远的路,使得 "最差的"情况下,可能绕了很远才能找到这个键值对。

为了平均寻址次数,有一个叫ROBINHOOD HASHING的方法出现了:

其主旨含义是"劫富济贫"。在数组中,额外记录一个数值代表寻址次数,当新加入一个元素

并与当前位置有冲突时,如果当前位置的寻址次数
小于新加入元素的寻址次数
时,就把
放在这个位置,让
原来那个位置的键值对再继续找新的位置。这种做法大大平均了寻址次数,使得"最差的没有那么差"。

题外话:在OI/ACM中,有用的不是哈希表本身,而是哈希函数的意义。可以通过(双)哈希去快速判断两个对象(字符串,矩阵,树...)是否相等。

注意树哈希有特别的方式……需要以后另开坑讲了。。。QAQ

推荐题目:

为了不影响选手的做题体验……我只是给出了"哈希"的关键字,所以也不算剧透题解啦(强行掩饰)

另外后面的两个题可能稍微有点难了……QAQ

大视野在线测评·欢迎您(BZOJ)

「BZOJ2351」 Matrix

「BZOJ3555」[Ctsc2014] 企鹅QQ

「BZOJ4754」独特的树叶

「BZOJ4892」[Tjoi2017]dna

ps:微信公众号是同名的,欢迎关注~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值