首页 > 其他分享 >Living-Dream 系列笔记 第57期

Living-Dream 系列笔记 第57期

时间:2024-05-18 23:19:35浏览次数:26  
标签:Living hash int 57 pos value long ull Dream

  • hash function(哈希函数)

    将一个规模很大的字符串用特定规则转化为特定数值,

    这种特定规则,我们称之为 hash function。

  • hash value(哈希值)

    字符串由哈希函数生成的数值。

  • hash collision(哈希冲突)

    多个字符串得到了相同的 hash value。

  • 算法竞赛中的 hash function

    通常将字符串转化为 \(x\) 进制的数。

  • hash value 过大的处理方法

    取模 / 自然溢出。

  • hash table(哈希表 / 散列表)

    字符串与其哈希值一一对应的表。

  • hash collision 的处理方法

    双哈希(算法竞赛常用) / 链表(工程领域常用)

  • 自然溢出

    直接让数字溢出,从而达到取模的效果。

    一般是 unsigned long long 自然溢出,此时数据溢出时会循环计数,即对 \(2^{64}\) 取模。

  • 预处理字符串每个子串的 hash value

    令 \(S\) 为字符串,\(X\) 为转化后的进制数,\(H_i\) 为前 \(i\) 个字符组成的字符串的 hash value,\(P_i\) 表示 \(X^i\)。

\[H_i=H_{i-1} \times X+S_i \]

\[P_i=P_i \times X \]

  • 截取 \(S\) 在区间 \([l,r]\) 的 hash value

\[H_r-H_{l-1} \times P_{r-l+1} \]

  • 删除 \(S\) 第 \(pos\) 个字符后的 hash value

\[H_{l,pos-1} \times P_{r-pos}+H_{pos+1,r} \]

注意 hash 的模数与进制尽量采用大质数,因为这样得出的 hash value 不容易两两间产生公因数,从而减少 hash collision 的发生。

T1

板子,没啥好讲的。

这里采用了三种写法:链表 / 双哈希 / STL map。

链表 实现
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long

const int N=1e6+5;
const int MOD=999983,BASE=26;
int n,ans=0;
string s;
vector<string> H[N];

int get_hash(string s){
	ull res=0;
	for(int i=0;i<s.size();i++) res=1ull*res*BASE+s[i];
	return res%MOD;
}

bool insert(string s){
	ull val=get_hash(s);
	for(auto i:H[val]) if(i==s) return 0;
	H[val].push_back(s); return 1;
}

int main(){
	cin>>n;
	while(n--){
		cin>>s;
		if(insert(s)) ans++;
	}
	cout<<ans;
	return 0;
}
双哈希 实现
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long

const int N=1e6+5;
int n,ans;
string s;
bool vis1[N],vis2[N];

ull get_hash(string s,int MOD,int BASE){
	ull res=0;
	for(int i=0;i<s.size();i++) res=res*BASE+s[i];
	return res%MOD;
}

int main(){
	cin>>n;
	while(n--){
		cin>>s;
		ull val1=get_hash(s,999983,26);
		ull val2=get_hash(s,333331,31);
		if((!vis1[val1])||(!vis2[val2]))
			ans++,vis1[val1]=vis2[val2]=1;
	}
	cout<<ans;
	return 0;
}
map 实现
#include<bits/stdc++.h>
#define int long long
using namespace std;

int n;
map<string,int> m;

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
    	string s; cin>>s;
    	m[s]++;
	}
	cout<<m.size();
	return 0;
}

T2

https://www.luogu.com.cn/article/mtopu73t

作业 T1

枚举删除位置算出 hash value 并与字典中单词的 hash value 比对即可。

code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long

const int N=1e6+5;
const int BASE=13331;
string x,y;
int n,m;
ull hash1,H[N],P[N];
int tot,ans[N];

void init_hash(){
	P[0]=1;
	for(int i=1;i<=n;i++) P[i]=P[i-1]*BASE;
	for(int i=1;i<=n;i++) H[i]=H[i-1]*BASE+x[i];
	for(int i=1;i<=m;i++) hash1=hash1*BASE+y[i];
}
ull get_hash(int x,int y){
	if(y<x) return 0;
	return H[y]-H[x-1]*P[y-x+1];
}

int main(){
	cin>>x>>y;
	n=x.size(),m=y.size();
	x='#'+x,y='#'+y;
	init_hash();
	for(int i=1;i<=n;i++){
		ull pre_val=get_hash(1,i-1);
		ull last_val=get_hash(i+1,n);
		if(pre_val*P[n-i]+last_val==hash1) ans[++tot]=i;
	}
	cout<<tot<<'\n';
	for(int i=1;i<=tot;i++) cout<<ans[i]<<' ';
	return 0;
}

作业 T2

我们仍然枚举删除的位置 \(pos\),并计算出删除后的 hash value \(val\)。

进行一个分讨:

若 \(pos < \frac{n+1}{2}\)(即 \(pos\) 在字符串的左半边),

由于 \(S\) 的长度为 \(\frac{n-1}{2}\),所以 \(S\) 在左半边不完整,

只能在右半边的区间 \([\frac{n+3}{2},n]\)(\(\frac{n+3}{2}=n-\frac{n-1}{2}+1\))取到。

若 \(pos = \frac{n+1}{2}\)(即 \(pos\) 在字符串的正中间),则 \(S\) 取左 / 右半边均可。

若 \(pos < \frac{n+1}{2}\)(即 \(pos\) 在字符串的左半边),

由于 \(S\) 的长度为 \(\frac{n-1}{2}\),所以 \(S\) 在右半边不完整,

只能在左半边的区间 \([1,\frac{n-1}{2}]\) 取到。

我们由此可以得出 \(S\) 及其 hash value \(valS\),

而两个 \(S\) 拼接在一起的 hash value 即为 \(valS \times P_{\frac{n-1}{2}}+valS\),

因此我们比较上式与 \(val\),若它们相等则找到了一个合法的 \(S\)。记录当前的 \(pos\)(为了循环结束后求答案,不能使用 substr,因为它的时间复杂度为 \(O(n)\)),累加答案个数,并标记 \(S\) 的 hash value(为了避免重复答案重复统计)。

循环结束后,按答案个数分别输出即可。

code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long

const int N=2e6+5;
const int BASE=999983;
int n,tot,mark;
ull H[N],P[N];
string u,ans;
map<ull,int> vis;

void init_hash(){
	P[0]=1;
	for(int i=1;i<=n;i++) P[i]=P[i-1]*BASE;
	for(int i=1;i<=n;i++) H[i]=H[i-1]*BASE+u[i];
}
ull get_hash(int l,int r){
	if(r<l) return 0;
	return H[r]-H[l-1]*P[r-l+1];
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>u,u='#'+u;
	init_hash();
	for(int i=1;i<=n;i++){
		ull val=get_hash(1,i-1)*P[n-i]+get_hash(i+1,n);
		ull half_val=(i>(n+1)/2?get_hash(1,(n-1)/2):get_hash((n+3)/2,n));
		if(val==half_val*(P[(n-1)/2]+1)){
			if(vis[half_val]) continue;
			vis[half_val]=1,tot++,mark=i;
			if(tot>1) puts("NOT UNIQUE"),exit(0);
		}
	}
	if(tot>0) cout<<(mark>(n+1)/2?u.substr(1,(n-1)/2):u.substr((n+3)/2));
	else puts("NOT POSSIBLE");
	return 0;
}

标签:Living,hash,int,57,pos,value,long,ull,Dream
From: https://www.cnblogs.com/XOF-0-0/p/18199926

相关文章

  • RK3576开发板NPU分享:探索6T强大性能,智能化应用无限可能!
    RKNNSDK快速上手指南开发板:ArmSoM-W3,ArmSoM-Sige7,ArmSoM-Sige5,ArmSoM-AIM7OS:Debian11/12目的:本文介绍如何使用rk的npusdk。作为瑞芯微8nm高性能AIOT平台,RK3576/RK3588NPU性能可谓十分强大,6TOPS设计能够实现高效的神经网络推理计算。这使得RK3576/RK3588在图像识别......
  • P6577 【模板】二分图最大权完美匹配 (KM)
    $\quad$初看就发现不对劲了,模板紫题,一看就不简单,就交了个裸\(KM\),哎,果然\(T\)了。$\quad$然后就是大力卡常(当然\(O(n^4)\))的复杂度不是卡常能解决的。遂看题解,发现一个据说\(O(n^3)\)的复杂度的\(KM\),也是非常抽象。具体解释详见https://www.luogu.com.cn/article/ip2m1gu......
  • 华为S5700交换机配下链路聚合
    学习目标  ·掌握接口速率的配置方法·掌握使用手动模式配置链路聚合的方法·掌握使用静态LACP模式配置链路聚合的方法·掌握在静态LACP模式下配置接口优先级的方法 拓扑图   图4.1以太网链路聚合拓扑图 场景 您是公司的网络管理员。现在公司购买了两......
  • AGC057C 做题记录
    题面看着很吓人!但是经过了一步步的思考,切完后再来看,其实也不过如此。纪念一下独立切的铜牌构造题。由于有\(+1\)操作,考虑反着建立01-trie,即以最低位作为第一个分支。这样\(+1\)操作相当于对最右边的一条链上每个点执行左右儿子交换。考虑trie树上每个叶子挂着对应数值在......
  • CH57x/CH58X/CH59X/CH32F/V208OTA使用说明
    目前提供了两种OTA升级方式,方式一:带库升级;每次升级可以带着库一起进行升级(带库升级适用于flash较大的芯片)方式二:固定库升级;升级时库不会随着升级而升级(适用于flash不够用时)方式一:升级时需要同时烧录这三个固件:(可以使用isp工具同时烧录也可以使用合并工具将三个工程合并后再烧......
  • loons2024年05月09日20:04:57
        1       1 11 1111 1111 1111 1111 1111 1111 ......
  • [题解]P1757 通天之分组背包
    P1757通天之分组背包分组背包模板题。总共\(s\)组,每组最多取一个物品,实际上就是一个物品总数为\(s\)的背包。for(inti=1;i<=s;i++){//枚举组 for(intj=1;j<=n[i];j++){//枚举每组的元素 for(intk=1;k<=m;k++){//枚举背包大小 f[i][k]=max(f[i][k],f[i-1][k]); if(......
  • C. Dreaming of Freedom
    原题链接题解有一个隐含逻辑,n个程序员会齐心协力地使保留的算法不止一个当\(m\geqn\)时,每个程序员各投一个,这样保留了n个算法当\(m\ltn\)时,如果想要不止保留一个算法,那么最后保留的算法一定能被n整除,也就是说,n一定有一个因子小于mcode#include<bits/stdc++.h>usi......
  • ENVI57扩展工具:FLAASH Easy-to-Use 大气校正易用版 [新]
    本扩展工具要求ENVI5.7及以上版本。低版本ENVI可以使用如下扩展工具:https://www.cnblogs.com/enviidl/p/16393415.html 自ENVI5.7版本开始,FLAASH大气校正功能提供了官方Task接口,详细信息可查看ENVI帮助内ENVI>Programming>ENVITasks>ListofTasks>FLAASH章节......
  • 洛谷P1576最小花费(逆Dijkstra算法)
    背景:说实话,这题有点考建模思想,及对Dijkstra算法的理解思路:因为转账间有手续费,即有一定损失比例,故边权均小于1(比例来说),而边的路权值非和,而是积,故边权相当于负(因为每次乘会使dis[i]变小)而题目刚好求最大路,而边权又等价于全为负,不刚好是Dijkstra的逆运用吗?原理:等......