注:
1. 本文章主要为了说明关于哈希函数的选取,但不说明为什么要这么选。
2. 只针对做题的哈希及哈希表的总结,不是工业加密的那个哈希。。
主要选取以下题目:
poj1840 poj3640 poj3349 poj2002 poj1200 poj2503 poj1496 poj3274 poj1077
哈希的作用我觉得就是判重和查找,其实是一个意思。如果题目要求判断数组,字符串,一个数,等等是否出现过,或者是否和另一个数组,字符串相等。这时候我们就可以利用哈希的特性,将其转换成一个数字,然后进行判断。
考哈希的题都有以上特点,那时只需用一个哈希函数转换一下就ok了,那么该如何转换呢?
- 数组的转换
int getkey(int *v,int k)
{
int i,p=0;
for(i=1; i<k; i++)
p=((p<<2)+(v[i]>>4))^(v[i]<<10);
p = p%prime;
if(p<0) p=p+prime;
return p;
}
- 字符串的转换(cf 835D(可参考我的另一篇博客))
unsigned long long p[maxn+10];//maxn为字符串的最长长度
unsigned long long S[maxn+10];
void init()
{
p[0]=1;
for(int i=1;i<=maxn;i++)
p[i]=p[i-1]*hashsize;
for(int i=1;i<=len;i++)
S[i]=S[i-1]*hashsize+a[i-1];
}
unsigned long long HS(int l,int r)
{
if(l==r)return a[l];
l++,r++;
return S[r]-S[l-1]*p[r-l+1];
}
- 从1到n的全排列
这个专业术语叫做康拓展开,但我感觉和hash如出一辙,将一个排列转换成一个0-n!-1的一个数。
int fact[10]; //fact[i] = i!
fact[0] = 1;
for(int i = 1; i < 9; ++i) fact[i] = fact[i-1]*i;
int kt(int s[], int n) { //n个数的排列s[0,n-1]
int ans = 0, cnt = 0; //返回其在全排列中的位置-1
for(int i = 0; i < n; ++i) {
cnt = 0; //cnt为在i后面出现的小于s[i]的数的个数
for(int j = i+1; j < n; ++j) if(s[j] < s[i]) ++cnt;
ans += cnt*fact[n-i-1];
}
return ans;
}
结语: 博主其实对哈希的理解非常浅薄,如果有更好的哈希函数,希望能和读者一起探讨,谢谢。