首页 > 其他分享 >哈希

哈希

时间:2024-02-01 09:59:13浏览次数:24  
标签:md 前缀 dfrac 枚举 哈希 字符串

【哈希】

哈希可以分成两块:哈希函数和哈希表。

哈希函数是一种对应关系,它可以把任意类型映射为一个不太大的整数。

例如字符串,我们可能希望在字符串上记录一些属性。但是字符串不能当下标,那我们就只能加个大常数用 map

这时,哈希函数出场了!如果我们有一个哈希函数 \(h()\) 可以把一个字符串 \(str\) 映射到 \(h(str)\) 这个整数上。

对于这个哈希函数 \(h(x)\),我们希望 \(h(x)\) 对于同样的 \(x\) 相等,对于不同的 \(x\) 一般不同。

因为 \(x\) 的取值范围一般比 \(h(x)\) 大,所以不同的 \(x\) 的 \(h(x)\) 相等是在所难免的。我们有两种方法处理这个问题:① 对相等的特殊处理。 ② 直接不管了,就直接把不同的 \(x\) 视为相同的。因为这样出问题的概率其实很小,如果刚好出 bug 算倒霉。

【字符串进制哈希】

把字符串视作一个 \(p\) 进制数。

例如 \(abcdahi\) 视作 \(p=31\) 进制数 \((0123078)_{31}\)。\(h(abcdahi)=0\times 31^6+1\times 31^5+\dots+8\times 31^0\).

但这样答案可能是很大很大的,所以有一个方法:设定一个数 \(md\),对 \(md\) 取模。

【模板】字符串哈希


那我们为什么对进制哈希情有独钟?因为进制哈希带来了一些和进制整数类似的性质,很方便

比如我们现在处理出 \(s\) 所有前缀的 Hash 值。(进制哈希方便处理前缀)那我们要查询 \(s\) 的子段的 Hash 值就很方便,\(h(s[l\sim r])=h(s[0\sim r])-h(s[0\sim l-1])\times p^l\).

而且两字符串拼接后的哈希值也能方便求出。

当然,哈希一般只判断是否相等,对于更复杂的信息,那就只能另寻他法了~


字符串前缀

注意是两个非空前缀。

基础想法:二重循环枚举两个前缀 + 一重循环判断是否是前缀,妥妥炸掉。

优化:先只用一重循环枚举一个前缀,第二个前缀位置可以二分(越大越好,越小越可行),判断是否是前缀可以哈希。

Radio Transmission

直接枚举前缀为循环节,然后枚举所有出现位置用哈希判断是否和循环节相等。

当枚举前缀长度 \(len=1\),最差枚举 \(\dfrac{n}{1}\) 次。

当枚举前缀长度 \(len=2\),最差枚举 \(\dfrac{n}{2}\) 次。

\(\dots\)

当枚举前缀长度 \(len=n\),最差枚举 \(\dfrac{n}{n}\) 次。

总时间复杂度 \(O(n(1+\dfrac{1}{2}+\dots+\dfrac{1}{n}))\).

而当 \(n\le 10^6\),后面的括号都不会超过 \(15\),所以可以看作常数。


当然,也可以利用性质:如果 \(x\) 长度是前缀,那么前 \(n-x\) 个和后 \(n-x\) 个相等。少一重枚举循环节开始位置的循环。

ANT-Antisymmetry

判断 \(s\) 中的子段是否是回文串:

  1. 建立 \(s'=reverse(s)\);

  2. 预处理出 \(s,s'\) 的前缀哈希;

  3. 如果一个子段在 \(s,s'\) 中的哈希值相等,就是回文子段。

\(O(n)\) 的预处理,\(O(1)\) 的查询。

这题里面类似处理:先全部取反,然后翻转。再在两个字符串里跑哈希。

但是 \(O(n^2)\) 枚举子串不行,我们可以用类似上面的优化:一重循环枚举对称轴 + 二分(越长越好,越短越可行)。

Compress Words

先对每个单词哈希。然后维护当前拼好的字符串的前缀哈希。

我们可以每次在拼上一个字符串时,前缀哈希递推——因为加字符只在末尾加。

查找最大匹配长度二分即可。

企鹅QQ

有点像电子字典

排序!!!

先排序,这样我们只用找出所有段,段内任意两个都相差 \(1\) 位。注意如果 \(a,b\) 差一,\(b,c\) 差一,\(a,c\) 不一定差一。所以我们要固定 \(a,b,c\) 相差的位置,才能保证 \(a,b,c\) 都是差一。

判断两个串是否差一,用哈希尝试删去某一个位置的字符后是否相等。

具体实现时可以预处理出所有字符串删去某个位置后的 Hash 值,存在一个数组里。

然后一重循环枚举删去哪个位置,找出所有段:段内删去这个位置后都相等。

【哈希思想】

解方程

记给定多项式为 \(p(x)\)。

先有暴力思路:枚举 \(O(nm)\),但是计算常数巨大。

我们可以改一改判断条件:\(p(x)=0\) 我们认为只要 \(p(x)\% md=0\) 就行。

我们把每个 \(a_i\) 看作一个字符串,求出它模 \(md\) 的 Hash 值,令 \(a_i\leftarrow h(a_i)\)。枚举解 \(x\),就用正常的方法,对应的 \(x^{?}\) 乘上已经变成 Hash 值的系数 \(h(a_i)\)。判断结果是否为 \(0\)。

还有一个专属于这种多项式题的优化:可以搞几个模数 \(md_{1\sim 5}\),我们要求 \(p(x)\%md_{1\sim5}=0\) 才看作是 \(0\)。另外,因为是多项式,所以 \(p(x)\equiv p(x+md_1)\pmod md_1\)。因此如果 \(p(x)\%md_1\) 不行,那 \(p(x+kmd_1)\) 也不行,打标记以后不枚举。

\(n\) 次解最多只有 \(n\) 个,这个优化效果显著。

星战

不可以,总司令

哈希的想法:把复杂数对应到简单数上。

那我们也可以把复杂操作对应到简单操作上,维护复杂数对应到维护简单数上!

设点 \(i\) 出边边权为 \(v_i\)(随机),维护边权和 \(y\)。

\(v[u]\) 是一条边的边权,\(s[u]\) 是现在点 \(u\) 的出边边权和,\(T[u]\) 是初始时点 \(u\) 的出边边权和。

① 删一条边,\(y-=v[u],s[v]-=v[u]\)

② 删一个点,\(y-=s[i]\)

③ 恢复一条边,\(y+=v[u],s[v]+=v[u]\)

④ 加一个点,\(y+=(T[u]-s[u]),s[u]=T[u]\)

出错的概率怎么算?

假设随机数范围 \(t\),查询次数 \(k\)。

单次不错 \(\dfrac{1}{t}\),\(k\) 次不错概率 \((1-\dfrac{1}{t})^k \approx 1 - \dfrac{k}{t}.\)

那么全部操作正确的概率,在我们随机数范围取 \(10^9\) 时就是 \(1-\frac{10^6}{10^9}\approx 99.9\%\).

【哈希表】

可以用 Hash 值做索引,搞数组字典。

优点:\(O(1)\),缺点:不能搞什么二分查找之类的,因为已经没有顺序了。

unordered_map 自己定义哈希函数模板:

struct my_hash {
	long long operator()(const vector<int> &v) const {
		long long res = 0, p = 131, md = 987656783;
		for (auto i: v)
			res = (res * p + i) % md;
		return res;
	}
};

unordered_map<vector<int>, int, my_hash> mp;

int main() {
	vector<int> a(3, 5);
	mp[a] = 5;
	cout << mp[a];
	return 0;
}

HDU3973

哈希可以通过长度和本身的哈希值,\(O(1)\) 合并!

这意味着我们可以用线段树维护!

\(val\) 设为一个二元组:\((hash,len)\) 哈希值和长度。

而查询是否存在,可以用 unordered_map 建立一个以哈希值为索引,布尔类型的哈希表。

标签:md,前缀,dfrac,枚举,哈希,字符串
From: https://www.cnblogs.com/FLY-lai/p/18000601

相关文章

  • 哈希碰撞
    哈希碰撞是指两个不同的输入数据在经过哈希函数运算后产生相同的哈希值。哈希函数通常将输入数据映射到一个较小的范围,比如一个固定大小的哈希码空间,但输入数据的范围可能远远大于哈希码空间。因此,多个不同的输入可能映射到相同的哈希码,这就是哈希碰撞的发生。哈希碰撞可能发生在......
  • 哈希表
    哈希表实现#include<stdio.h>#include<stdlib.h>#include<string.h>#defineHASHTABLE_CAPACITY20//===File:array_hash_map.c===/*键值对int->string*/typedefstruct{ intkey; char*val;}Pair;typedefstruct{ void*set; intlen;......
  • 哈希学习笔记
    定义与基本求法定义:用于用一个进制数表示一个字符串,以方便存储和判断两字符串是否相等。基本求法:联系十进制,如\(1234\)即\(1\times10^3+2\times10^2+3\times10+4\)同样的对于一个字符串,去一个大于其中任意字符(\(\text{ASCII}\)码)的数\(base\)作为进制。也就有了......
  • 【板子】字符串哈希
    //lgp3370//Copyrightyeyou26#include<bits/stdc++.h>usingnamespacestd;#defineullunsignedlonglongstrings;intn;constullp=998244353;ullnow_hash;ullv[100005];intcnt;intans;voidget_hash();voiddo_compare();voidinit()......
  • 哈希排序
    #include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intn,a[1000009];inlinevoidread(registerint&a){a=0;charc;while((c=getchar())<48);doa=(a<<3)+(a<<1)+(c^48);while((c=getchar())>47);}......
  • 哈希表
    目录895最大频率栈优化版本v1优化版本v2看题解了884846一手顺子895最大频率栈复杂度爆了简述一下思路:用栈来存入栈元素,用哈希表来存出现次数,用一个frequency来记录最大出现次数遍历栈,将栈顶元素放到另一个临时栈中,如果栈顶元素的出现次数=frequency,那么说明是最大元素,我就......
  • 哈希学习笔记+杂题(进阶1 字符串哈希)
    哈希杂题前言:竟然下雪了,但是天是灰蒙蒙的。一、哈希学习笔记+杂题(进阶1字符串哈希)相关题单:戳我字符串哈希因为是一种玄学做法,所以具有极强的延展性。所以再碰到字符串的题时,抛开马拉车,kmp,字典树,AC自动机,SA&SAM,先想一下哈希的做法,如果时间复杂度允许,那就可以直接上哈希(虽然你......
  • 哈希学习笔记+杂题(进阶1 字符串哈希)
    哈希杂题前言:竟然下雪了,但是天是灰蒙蒙的。一、哈希学习笔记+杂题(进阶1字符串哈希)相关题单:戳我字符串哈希因为是一种玄学做法,所以具有极强的延展性。所以再碰到字符串的题时,抛开马拉车,kmp,字典树,AC自动机,SA&SAM,先想一下哈希的做法,如果时间复杂度允许,那就可以直接上哈希(虽然你......
  • 哈希学习笔记+杂题(基础2 字符串哈希)
    哈希杂题前言:骗分神器,我之前竟然没有学。一、哈希学习笔记+杂题(基础2字符串哈希)相关题单:戳我1.哈希(hash)简介哈希算法(HashAlgorithm),又称散列算法。有两种用法,第一种就是将一字符串转化成任意进制的数,目的是方便存储。第二种就是将大范围的数映射成小范围的数,目的也是方便存......
  • 线性表(散列)- 哈希表
    哈希表散列表(Hashtable,也叫哈希表),是根据关键码值(Keyvalue)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表HashSetandHashMap哈希表使用O(N)空间复杂度存储数据,并......