bzoj3555——玄学hash

本文介绍了一种基于字符串的Hash算法,通过预处理实现O(n)的时间复杂度,并能在O(1)时间内完成字符串匹配。文章详细解释了如何计算字符串的Hash值及子串的Hash值,同时探讨了如何减少Hash碰撞的概率,包括选择合适的素数和模数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符串hash
往往的是暴力分
当然写的好的话运气不是很差的话说不定能AK
O(n) 预处理 O(1)匹配
对于一个长度是n的字符串
设字符集大小为m,默认字符串的长度是s[1...n];
预处理出p[a] = p^a;
那么我们可以计算前缀的hash值 
hsh[1] = s[1] * p[1];
hsh[2] = s[2] * p[2] + hsh[1];
hsh[i] = s[i] * p[i] + hsh[i-1];
那么怎么查询子串l.....r
 - gethsh(l,r) = hsh[r] - hsh[l-1] * p[l - r + 1]
高中数学就可以推出,这里我就不说了
一般的我们认为子串的hsh值相等那么这两个子串就相等
显然的会有一定的出错概率
而且hash的数会溢出
自然溢出是不太好的
建议对他取模 
好,以上是科学,下面讲玄学
对于p的取法 只要是大于M的一个素数就好
比如我常用的233
对于模数
某老师曾说过,需要一个妹子的生日作为模数
因此他从来没有被卡掉过
但是我的妹子的生日显然的不是一个素数
没关系卡掉就卡掉吧我也不在乎
事实上我也没有被卡掉过几次
总之
学习科学使用玄学
bzoj3555一道经典的hash题
很暴力的一道题
枚举每一位,计算去掉该位后的hsh值
边放边统计
#include<iostream>
#include<algorithm> 
#include<cstdio>


const int N = 30030;


#define ll unsigned long long 


const int M = 20010520;


using namespace std;


int n,l,m;
ll k[N];


struct str{
char s[210];
ll f[210],g[210];
inline void init(){
scanf("%s",s+1);
for(int i = 1;i <= l;i++){
f[i] = f[i - 1] * 233 % M + s[i];
}
for(int i = l;i >= 1;i--){
g[i] = g[i+1] * 233 % M + s[i];
}
}
inline ll hashdel(int x){
return f[x-1] * 233 % M + g[x+1] *233 %M
}
}a[N];


int main(){
cin>>n>>l>>m;
for(int i = 1;i <= n;i++){
a[i].init();
}
int ans = 0;
for(int p = 1;p <= l;p++){
for(int i = 1;i <= n;i++){
k[i] = a[i].hashdel(p);
}
sort(k+1,k+1+n);
int t = 1;
for(int i = 2;i <= n;i++){
if(k[i] == k[i-1]){
ans = ans + t;
t++;
}
else{
t = 1;
}
}
}
cout<<ans;
return 0;
}
总之无论是随机数还是hsh在后面加妹子生日会得更多的分的
QAQ不是我说的 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值