HASH-TABLE是什么:
HASH的优势:
开一个长度为L的数组,平摊查找速度提到L倍,搞多个键值让HASH的结果不相等,但最终放到数组里布碰撞是由数组大小决定的。,以(%n)来计算,达到sqrt(n)时,由50%的碰撞概率(摘自夹克老爷)
简单来说:
HASH就是乱搞,把大范围的离散数据通过一个特定的生成函数转化为一个小范围的定长数据中的一个槽(slot)
为啥是乱搞?因为这个生成函数所产所得数据取决于你的base 和 bigprime (如果你是用取模的生成函数的话),其他同理
----
有同学说,基数看命,命好,1A:ORZ
之前,看到有一个素数表可供参考:素数选择
一个字符串HASH函数:
----也是Rabin–Karp algorithm(也叫滚动HASH)的生成函数
一道例题(SPOJ-NHAY):
一、KMP前缀数组做法(纯裸。。)
二、Rolling HASH:(滚动hash)--著名的Rabin–Karp algorithm
CODE(二):
#include <cstdio>
#include <algorithm>
#include <cstring>
const int MAXN = 1e7+10;
typedef unsigned long long ull;
int n;
char p[MAXN],t[MAXN];
void gundonhash(char *p,char *t)
{
int m=strlen(t);
if(n==0||n>m)
return;
int prime=26;
int mod=97;
int tmp=1;
int hashp=0,hasht=0;
for(int i=0;i<n;++i) tmp=(tmp*prime)%mod;
for(int i=0;i<n;++i)
{
hashp=(hashp*prime+p[i])%mod;
hasht=(hasht*prime+t[i])%mod;
}
for(int i=0;i+n<=m;++i)
{
int flag=0;
//printf("%I64d %I64d\n",hashp,hasht);
if(hashp==hasht)
{
flag=1;
for(int j=0;j<n;++j)
{
if(p[j]!=t[j+i])
{
flag=0;
break;
}
}
if(flag)
printf("%d\n",i);
}
if(i<m-n)
hasht=(hasht*prime-t[i]*tmp+t[i+n])%mod;
if(hasht<0)
hasht+=mod;
}
}
int main()
{
while(~scanf("%d",&n))
{
getchar();
for(int i=0;i<n;++i)
scanf("%c",&p[i]);
scanf("%s",t);
gundonhash(p,t);
printf("\n");
}
}
/*
2
na
banananobano
6
foobar
foo
9
foobarfoo
barfoobarfoobarfoobarfoobarfoo
*/
对这一题的一个解释:(过程的可以直接跳过,非常基础的初等数学)
首先,我们先计算一个幂------tmp:
pattern是模式串,tmp是BASE的模式串长度的幂次
然后我们计算两个初始HASH:
一、模式串的HASH:
二、文本串的HASH:
最后,开始遍历文本串:
关于else部分的证明:(比较重要)
由1.2,易得:
简而CODE之:
bool gundongHASH(char *p,char *t)
{
int n=strlen(p);
int m=strlen(t);//计算长度
if(n>m)
return 0;//模式串长度大于文本串长度,之间返回
int base=256;//base值
int mod=97;//mod值
int tmp=1;//幂
for(int i=0;i<n;++i) tmp=(tmp*base)%mod;//计算幂
int hashp=0,hasht=0;//两个初始hash
for(int i=0;i<n;++i)
{
hashp=(hashp*base+p[i])%mod;
hasht=(hasht*base+t[i])%mod;//计算两个hash
}
for(int i=0;i<=m-n;++i)
{
int flag=0;//设置标记
if(hashp==hasht)
{
flag=1;
for(int j=0;j<n;++j)//为啥还要再一个个字符判断,因为可能有冲突
if(p[j]!=t[i+n])
{
flag=0;
break;
}
if(flag)
return 1;//理论上,可以直接打这一行
}
hasht=(hasht*base+t[i+n]-t[i]*tmp)%mod;
}
retunr 0;
}
不得不强调的一个东西:
HASH的易操作性使得不同的KEY可能会映射相同的槽(slot),这被称为冲突.
所以,即使有两个HASH值相同,也不意味着这两个HASH对应的KEY一定相同。