HASH-TABLE 的简单介绍

HASH-TABLE是什么:

 hash-table

HASH的优势:

开一个长度为L的数组,平摊查找速度提到L倍,搞多个键值让HASH的结果不相等,但最终放到数组里布碰撞是由数组大小决定的。,以(%n)来计算,达到sqrt(n)时,由50%的碰撞概率(摘自夹克老爷)

简单来说:

HASH就是乱搞,把大范围的离散数据通过一个特定的生成函数转化为一个小范围的定长数据中的一个槽(slot)

为啥是乱搞?因为这个生成函数所产所得数据取决于你的base 和 bigprime (如果你是用取模的生成函数的话),其他同理

----

有同学说,基数看命,命好,1A:ORZ

之前,看到有一个素数表可供参考:素数选择

一个字符串HASH函数:

HASH=(HASH*BASE+S[i])MOD (BIGPRIME)----也是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:tmp=base^{pattern.lenth}

pattern是模式串,tmp是BASE的模式串长度的幂次

然后我们计算两个初始HASH:

一、模式串的HASH:

i:0\rightarrow pattern.lenth-1:

HASH_{pattern}=(HASH_{pattern}*BASE+P[i])MOD(BIGPRIME)

二、文本串的HASH:

i:0\rightarrow text.lenth-1:

HASH_{text}=(HASH_{text}*BASE+T[i])MOD(BIGPRIME)

最后,开始遍历文本串:

i:0\rightarrow text.lenth-pattern.lenth:

If:HASH_{pattern}=HASH_{text}\Rightarrow mathc /success

else:HASH_{text}=(HASH_{text}*BASE+T[i+pattern.lenth]-t[i]*tmp)MOD (BIGPRIME)

关于else部分的证明:(比较重要)

1.HASH_i=T_i*base^{m-1}+T_{i+1}*base^{m-2}+......+T_{i+m-2}*base+T_{i+m-1}

2.HASH_{I+1}=T_{i+1}*base^{m-1}+T_{i+2}*base^{m-2}+......+T_{i+m-1}*base+T_{i+m}

由1.2,易得:

HASH_{i+1}=HASH_{i}*base-T_{i}*tmp+T_{i+m+1}

tmp=base\cdot base\cdot ...\cdot base( pattern.lenth /of/ base )

简而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一定相同。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值