一,哈希表
我们设置一个函数(称为哈希函数),将值映射到这个值通过哈希函数得到的下标位置
- 哈希表实现:
- 给定一个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. 两种防冲突方案
双哈希(双模数) 分别用两组
base+mod算出一对哈希,只有两对全部相等才判定字符串相同。 常用模数:10^9+7,10^9+9,998244353,固定模数可推导公式,也会被卡自然溢出(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−1和L2,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");
}
}
三,终章
最后一天!祝大家天天开心