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");
	}
}

三,终章

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

相关推荐
FakeOccupational3 分钟前
【电路笔记 STM32】stm32f103 实现HAL_UART_Transmit_DMA + 普通模式/连续模式的 UART DMA 收发
笔记·stm32·嵌入式硬件
北京海得康1 小时前
DMD基因治疗deramiocel遭FDA质疑,Ultragenyx DTX401即将获批
笔记
zjnlswd1 小时前
C#学习笔记2 刘铁锰
笔记·学习
Mr_liu_6662 小时前
Claude非专业入门实战笔记(4):Claude运行方式简单解析——从RAG到代理循环
笔记·ai编程·claude
凯尔萨厮2 小时前
Git(学习笔记)
笔记·git·学习
蒸蒸yyyyzwd2 小时前
cpp选手备注秋招 MySQL学习笔记 day8
笔记·学习·求职招聘
AI直播技术杂谈2 小时前
本周技术笔记:直播推流的心跳检测与断线重连逻辑
笔记
九硕智慧建筑一体化厂家2 小时前
从照明节能到碳排可见:能碳平台助力机房迈向 “零碳” 未来
运维·笔记·智慧城市
KhalilRuan4 小时前
UnityCsReference——笔记
java·开发语言·笔记
shangwen1234 小时前
食品厂无尘车间装修施工工艺笔记:C型钢吊顶加固方案尚文净化
笔记