y1,y2总复习笔记10 2026.7.24

一,哈希表

我们设置一个函数(称为哈希函数),将值映射到这个值通过哈希函数得到的下标位置

  • 哈希表实现:
  • 给定一个key(键)
  • 通过哈希函数 计算:哈希值 = hash(key)
  • 哈希值对数组长度取模,得到数组下标
  • 直接把 value 存到对应下标位置

若不同的值通过哈希函数映射到了同一个下标,这样就产生了哈希冲突

解决方案有两个:

电影院有人占位?

我的位被占了,那我就坐原本位置旁边没人的位置------开放寻址法

当前位置被占,往后依次找空位置存放

我坐他腿上!------拉链法

冲突位置挂一个链表 / 红黑树

初赛知识补充:装填因子

装填因子阿尔法=元素个数除以哈希表长度

  • α 越小:冲突越少,空间浪费大
  • α 越大:冲突越多,查询变慢

二,字符串哈希

字符串的相等比较是非常慢的

我们如何快速判断相等呢?把任意长度字符串映射成一个数字哈希值直接进行比较而减少时间复杂度,我们把这个过程叫做字符串哈希

实现:前缀哈希

base:基数 也是进制,常用131,147

前缀哈希数组 ,代表前 i 个字符即为前缀i的哈希值

base 幂次数组

公式如下,val(si)即为字符哈希值就是si

L到R的哈希值

首先要处理一下幂次

然后递推公式

注意取模!不同的base要搭配不同的模数

cpp 复制代码
const int N=1e6+5;
const ll mod1=1e9+7,mod2=998244353;
int n,q,L1,L2,k;
array(ll,2)	pw[N],has[N];
char s[N];
void init(){
	pw[0][0]=pw[0][1]=1;
	for(int i=1;i<N;i++){
		pw[i][0]=pw[i-1][0]*131%mod1;
		pw[i][0]=pw[i-1][1]*147%mod2;
	}
	has[0][0]=has[0][1]=s[0];
	for(int i=1;s[i];i++){
		has[i][0]=(has[i-1][0]*131+s[i])%mod1;
		has[i][1]=(has[i-1][1]*147+s[i])%mod2;
	}
} 
array<ll,2> getHash(int L,int R){
	ll h1,h2;
	if(!L){
		h1=has[R][0],h2=has[R][1];
	}
	else{
		h1=(has[R][0]-has[L-1][0]*pw[R-L+1][0]%mod1+mod1)%mod1;
		h2=(has[R][1]-has[L-1][1]*pw[R-L+1][0]%mod2+mod2)%mod2;
	}
	return {h1,h2};
}

字符串哈希也有哈希冲突

1. 单模数容易撞哈希

不同字符串算出相同哈希值 = 哈希冲突,会导致误判相等。

2. 两种防冲突方案

  1. 双哈希(双模数) 分别用两组 base+mod 算出一对哈希,只有两对全部相等才判定字符串相同。 常用模数:10^9+7,10^9+9,998244353,固定模数可推导公式,也会被卡

  2. 自然溢出(unsigned long long) 不手动取模,利用 ull 溢出自动对取模,代码简洁;非常快速但容易被卡

那有什么绝不会被卡呢:随机模数双哈希!

cpp 复制代码
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
uniform_int_distribution<long long> dis(1000000000LL,2000000000LL); 
bool isPrime(int x){
	for(int i=2;i*i<=x;i++){
		if(x%i==0)	return false;
	}
	return true;
}
void getMod(ll& mod){
	mod=dis(rnd);
	if(!(mod&1))	mod++;
	while(!isprime(mod))	mod+=2;//+1偶数没必要 
}

该代码可以取出非常大的随机数

例题:字典序比较

给定一个字符串s,进行q次询问,每次询问给出三个值:L1,L2,k,代表L1,L1+k−1L2,L2+k−1两个子串,请你比较两个子串的字典序。如果第一个子串大,输出big,如果第一个子串小,输出small,如果相等,输出equal。

找两个子串的最长公共前缀,根据字典序比较规则,那么后面一位的大小将决定两个字符串的大小

找最长公共前缀:一定有一个临界点,其左侧(包括该点)满足两个字符串相等,右侧一定不相等,该点唯一,考虑二分最大化答案找临界点(10型二分),判断相等考虑字符串哈希

cpp 复制代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
const ll mod1=1e9+7,mod2=998244353;
int n,q,L1,L2,k;
array<ll,2>	pw[N],has[N];
char s[N];
void init(){
	pw[0][0]=pw[0][1]=1;
	for(int i=1;i<N;i++){
		pw[i][0]=pw[i-1][0]*131%mod1;
		pw[i][1]=pw[i-1][1]*147%mod2;
	}
	has[0][0]=has[0][1]=s[0];
	for(int i=1;s[i];i++){
		has[i][0]=(has[i-1][0]*131+s[i])%mod1;
		has[i][1]=(has[i-1][1]*147+s[i])%mod2;
	}
} 
array<ll,2> getHash(int L,int R){
	ll h1,h2;
	if(!L){
		h1=has[R][0],h2=has[R][1];
	}
	else{
		h1=(has[R][0]-has[L-1][0]*pw[R-L+1][0]%mod1+mod1)%mod1;
		h2=(has[R][1]-has[L-1][1]*pw[R-L+1][1]%mod2+mod2)%mod2;
	}
	return {h1,h2};
}
bool check(int x){
	array<ll,2> t1=getHash(L1,L1+x-1),t2=getHash(L2,L2+x-1);
	return t1==t2;
}
int main(){
	scanf("%d%s",&q,s);
	n=strlen(s);
	init();
	while(q--){
		scanf("%d%d%d",&L1,&L2,&k);
		L1--,L2--;
		int L=0,R=k;
		while(L<R){
			int mid=L+R+1>>1;
			if(check(mid))	L=mid;
			else R=mid-1;
		}
		if(L==k)	printf("equal\n");
		else if(s[L1+L]<s[L2+L])	printf("small\n");
		else printf("big\n");
	}
}

三,终章

最后一天!祝大家天天开心

相关推荐
MuMuMu12232 小时前
智慧监测的“火眼金睛”:当AIoT重构环保监管体系
笔记
一只小菜鸡..3 小时前
南京大学 操作系统 (JYY) 学习笔记:进程地址空间与内存“外挂”魔法
笔记·学习
一只小菜鸡..4 小时前
南京大学 操作系统 (JYY) 学习笔记:访问操作系统的对象与“一切皆文件”
笔记·学习
摇滚侠10 小时前
《RocketMQ 官网》阅读笔记 RocketMQ 消息队列 MessageQueue 消息 Messagege
笔记·rocketmq
Ztt66666666610 小时前
【无标题】
经验分享·笔记·其他·百度·微信公众平台
依然范特东11 小时前
强化学习笔记4--TD算法
笔记
AOwhisky11 小时前
Linux(CentOS)系统管理入门笔记(第十五期)——文件系统与存储管理(上篇):设备识别、挂载与文件查找
linux·运维·笔记·centos·文件系统
晓梦林11 小时前
[极客大挑战 2019]PHP学习笔记
笔记·学习·php
YUS云生12 小时前
Python学习笔记·第39天:提示词工程——更好提问的五大技巧与JSON数据格式
笔记·python·学习