hash算法(哈希)

hash 算法
什么hash 算法 ?
简单地来讲就是将字符串转化为一个整数。它一般应用于字符串的场景中。
现在我们来讲hash算法。
hash 公式
hash[i]=(hash[i-1]*base+str[i]-‘a’+1)%mod ;
base的值是随机的,意思就是你自己可以去选择任意的数,但是按照经验值表明,一般base的值和mod的值要尽量大,这样冲突的概率是最低的。
但是经验告诉我们base=131或者13331是最好的。
其次mod我们也可以省略,只要我们利用unsigned long long的范围自然溢出,相当于自动取模。
乍一看是不是有点难以理解。
我们现在就来分析一下。
举一个栗子str[]={ “abcdefg”};
因为a—>z有26个字母,因此我们可以将一个字符串看成一个26进制的数,然后我们的hash值就等于这个数的10进制值,所以我们就要想法设法得求出这个值,并且得到一些推论和应用。
首先我们定义一个unsigned long long int hash[1001]的一个用来存储hash值的数组。因此我们可以省略取模的步骤。
hash[0]=0;
当 i =1时,hash[1]=hash[1-1] *base+str[1]-‘a’+1=0 *131 +‘a’-‘a’+1=1;
当 i =2时,hash[2]=hash[2-1] base+str[2]-‘a’+1=1131 +‘b’-‘a’+1=132;
当 i =3时,hash[3]=hash[3-1] *base+str[3]-‘a’+1=132 *131 +‘c’-‘a’+1=17295;
依次类推…
其实很容易就发现,我们的每一个hash值都与前缀的hash值是一一对应的。
“a”的hash值=hash[1];
“ab”的hash值=hash[2];
“abc”的hash值=hash[3];

那么我们已知 ab的hash值(也就是hash[2]的值)和abcde的hash值(也就是hash[5]的值)要我们求bcde的hash值。
推论公式:hash值=hash[right]-hash[left-1] *base的(right-left+1)次方。
因此
bcde的hash值=hash[5]-hash[2-1] *base的(5-2+1)次方
数学理论可以自己私下去推导嘛!
现在我们看例题
很久很久以前,森林里住着一群兔子。
有一天,兔子们想要研究自己的 DNA 序列。
我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。
然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。
注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。
输入格式
第一行输入一个 DNA 字符串 S。
第二行一个数字 m,表示 m 次询问。
接下来 m 行,每行四个数字 l1,r1,l2,r2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。
输出格式
对于每次询问,输出一行表示结果。
如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)。
数据范围
1≤length(S),m≤1000000
输入样例:
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
其实运用我们那两个公式就能求出来。
解题思路:我们把每个前缀的hash值求出来,然后运用推导公式求出部分字符串的hash值,然后对比,相等即YES 反之则NO。
代码如下

#include<stdio.h>
#include<string.h>
char str[1000010];
#define base 131//取base为131;
typedef unsigned long long int ULL;//太长了,这样方便一些;
ULL hash[1000010],p[1000010];//定义一下,hash数组用来存放各个前缀的hash值,而p数组用来存放base的各个次方的值;
int get(int l,int r)//自定义函数用来求各部分字符串的hash值;
{
    return hash[r]-hash[l-1]*p[r-l+1];//推导公式;
}
int main()
{
    int n,m,i,l1,r1,l2,r2;
    scanf("%s",str+1);
    n=strlen(str+1);
    hash[0]=0;
    p[0]=1;
    for(i=1; i<=n; i++)//求出各个前缀的hash值;
    {
        hash[i]=hash[i-1]*base+str[i]-'a'+1;//hash值公式;
        p[i]=p[i-1]*base;//base的次方;
    }
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
        if(get(l1,r1)==get(l2,r2))//进行对比;
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

总结:相对于kmp算法来说,此算法易于理解,且简单,kmp难懂难记。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值