hash入门(好像找不到门)
HASH一般概念
Hash其实是一种散列技术,散列技术是指在记录的存储位置和它的关键字之间建立一个确定的对应
关系f,使每一个关键字都对应一个存储位置。即:存储位置=f(关键字)。这样,在查找的过程中,
只需要通过这个对应关系f 找到给定值key的映射f(key)。只要集合中存在关键字和key相等的记录,
则必在存储位置f(key)处。我们把这种对应关系f 称为散列函数或哈希函数。
哈希冲突
•在理想的情况下,每一个 关键字,通过哈希函数计算出来的地址都是不一样的。但是在实际情况中,
我们常常会碰到两个关键字key1≠key2,但是f(key1) = f(key2), 这种现象称为冲突,并把key1和key2称
为这个散列函数的同义词。
下面将以两个类型的例题进行具体分析(就算分析了,实际操作还是。。。)
例题一,Equations
题意大概就是给你a, b, c, d四个数,求有多少种x1,x2,x3,x4组合让上面写的等式成立。
要求组合,自然想到枚举判定。但是如果套4层循环的话很显然会tle,所以我们要优化枚举方案。直接看代码吧,内有注释。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
const int MOD=1e6+1;
int hz[MOD], hf[MOD];
//一个负责正数一个负责负数
int main(){
int a, b, c, d;
while(scanf("%d %d %d %d", &a, &b, &c, &d)!=EOF){
//多组读入
int ans=0;
//答案初始化
if((a>0&&b>0&&c>0&&d>0)||(a<0&&b<0&&c<0&&d<0)){
//这两种情况下特判直接输出能提高运行速度
printf("0\n");
continue;
}
memset(hz, 0, sizeof(hz));
memset(hf, 0, sizeof(hf));
//初始化两个hash数组
//下面分两组进行枚举
for(int i=1;i<=100;i++){//对a*x1*x1+xb*x2*x2进行枚举
for(int j=1;j<=100;j++){
int x=a*i*i+b*j*j;//计算出结果
if(x>0) hz[x]++;//将正数结果存入正数hash
else hf[-x]++;//将负数存入复数hash
}
}
for(int i=1;i<=100;i++){//对c*x3*x3+d*x4*x4进行枚举判定
for(int j=1;j<=100;j++){
int x=c*i*i+d*j*j;//计算出结果
if(x<0)ans+=hz[-x]*16;//为负数就往前面的正数里面找若有则表示护卫相反数能满足式子
else ans+=hf[x]*16;//为正就向负hash里面找
//因为我们x1,x2,x3,x4都是从1到100来枚举的
//而题目里需要-100到100,我们排列组合一下就知道
//当都为正满足时x1,x2,x3,x4各自不同取正负结果又16种
//结果都加到ans答案里
}
}
printf("%d\n", ans);
}
return 0;
}
例题二,Extend to Palindrome
题目大意就是每给你一个字符串就让你输出它的(最短)回文串,当然如果原本就是就直接输出。
下面是字符串hash用到的hash数组暂且先看看,接着就是代码
•Hash[r]=s[1]*pr+s[2]*pr-1…s[r]*p0
•Hash[l-1]=s[1]*pl-1+s[2]*pl-2…s[l-1]*p0
•Hash[l~r]=s[l]*pr-l+s[l+1]*pr-l-1…s[r]*p0
void get_hash(){
return ((hash[r]-hash[l-1]*pow(p,r-l+1))%mod+mod)%mod;
}
AC代码,内附解析
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
char s[1111111];
unsigned long long h_op[1111111], h_ed[1111111], po[1111111], p = 131;
//依次分别是从前往后的字符hash,从后往前的字符hash,需使用到的参数用来得到字符段的hash,自定mod参数
unsigned long long get_op(int l, int r){
//得到从前往后的字符串hash
return h_op[r]-h_op[l-1]*po[r-l+1];
}
unsigned long long get_ed(int l, int r){
//得到从后往前的字符串hash
return h_ed[r]-h_ed[l-1]*po[r-l+1];
}
int main() {
while (scanf("%s", s + 1) != EOF) {
//读入字符串,s+1表示字符串从下标1开始读起(或者你自己百度一下啥意思)
int l = strlen(s + 1);
//得到字符串长(因为上面+1了,所以这里也要+1)
po[0] = 1, h_op[0]=0, h_ed[0]=0;
//为下面存入,读取hash做准备
for (int i = 1; i < 1111111; i++) {
po[i] = po[i - 1] * p;
}
//将po与处理一下(其实可以放到外面缩短程序运行时间,但是能A咱先放一放,懒得改了)
for (int i = 1; i <= l; i++) {
h_op[i] = h_op[i - 1] * p + s[i];
//从前往后存入字符串hash
h_ed[i] = h_ed[i - 1] * p + s[l - i + 1];
//从后往前存入字符串hash
}
int minlen = 0;//从这个位置+1以后开始是回文串
for (int i = 1; i <= l; i++) {
unsigned long long op = get_op(i, l);
//得到从前往后的字符hash
unsigned long long ed = get_ed(1, l-i+1);
//得到从后往前的字符hash
if (op == ed) {//当其相等是表示到了回文串区间
minlen = i - 1;//i-1就不是回文串区间,存入minlen
break;//退出循环
}
}
printf("%s", s+1);现将原字符串打印
for (int i = minlen; i >= 1; i--) {
//再从minlen位置往前输出字符,最后得到的便是回文字符串了
printf("%c", s[i]);
}
printf("\n");
}
}