字符串hash

hash的定义

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-

image)通过散列算法变换成固定长度的输出,该输出就是散列值。hash是一种思想。这种

转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列

成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消

息压缩到某一固定长度的消息摘要的写法。

字符串哈希

字符串哈希只是多种哈希的一种,即把一串字符串用一个固定长度的值代表,所以数字是代表

的最好选择,因为一个字符串有多个字符,基本上做一些事情的时间复杂度是O(n),而拿一个

数字代表,因为数字的操作基本上都是O(1),从而大大减少了时间复杂度。怎么拿一个数字代

表一个字符串呢,下面我具体解释解释:

我们学过10进制的都应该知道一个数,比如123,它的十进制值是等于

1 * 10 2 + 2 * 10 1 + 3 * 100

再比如8进制的123,它的十进制值是等于

1 * 8 2 + 2 * 8 1 + 3 * 80

所以字符串哈希(hash)就是把字符串当做一个进制数,假设他是P进制,因为字符本身就有

ASCALL值,所以不想复杂的值,假设该字符串只有小写字符(如果想要大写字母和小写字母都

存在,可以自己编写一个函数返回自己想要的字母代表的值)因为该字符串只有小写字符所以

我们可以设a ~ z代表1 ~ 26 为什么没有0呢,因为你不知道第一个字母是哪个,如果第一个字母

是0就没有意义了,而且这只是作为一个映射,只需要1 ~ 26就足够了。那我们的进制取哪个数

好点呢,进过很多大佬的证明最好去素数,而且是131或13331这俩个数最好,不要问为什么是

这俩个数,我也不知道,用就行了。因为字符串有时候会非常非常长,这个时候就会溢出,任

何类型都不能接住,所以我们要选择性溢出,取模一个数,然后又有大佬的自然溢出思想,就

是无符号长整型,因为无符号型是没有负数的,所以无符号长整型是264是最大值,即会自动

对这个值取模,就是说将hash的值存入无符号长整型类型的变量里面就行了。

上面解释了怎么求字符串的代表的数字,在这里假设P为131,所以可以得到公式:

**hash[i]=hash[i−1]∗131+id(s[i])**这里id(s[i])代表该字符代表的数值。

举个例子 字符串 :abc

abc的值就是 a * 1312 + b * 1311 + c * 1310

假设字符串从1开始,hash[0]的值就为0,按照公式计算就是:

把它分成三个字符串就是a ab abc

a的值就是 hash[0] * 131 + 1

abc的值就是 hash[1] * 131+ 2

abc的值就是 hash[2] * 131 + 3

以上计算方法同进制化十进制计算方法。

接下来我们讲解一下应用到的题。

例题

题目:兔子与兔子

很久很久以前,森林里住着一群兔子。

有一天,兔子们想要研究自己的 DNA 序列。

我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英

文字母)。

然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两

个兔子是否一模一样。

注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。

输入格式

第一行输入一个 DNA 字符串 S。

第二行一个数字 m,表示 m 次询问。

接下来 m 行,每行四个数字 l1,r1,l2,r2l1,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
题解:

这个题要是用一般的思想就是一个一个比较来判断是不是一样,但是长度有100万之长,一组数

据还好,数据一旦多起来就会时间超限,所以我们用上面提及的字符串哈希来做此题,因为字

符串哈希把字符串当做一个进制来处理,所以我们把这几个数圈到的数取出再比较取出后的数

即可。取出的的方法就是将前面的全部减去,是不是很简单?

列如样例的 aabbaabb 首先输入1 3 5 7,怎么取出1 3呢,因为1 3就是aab,前面并没有东西,

所以直接取出即可,5和7呢,因为5和7在数组的此位置aabb(aab)b,取出就将aabb减去就可以

了,注意减去的时候要乘上P的减留下的位的次方,就比如123456789,你要取出789,是不是

需要减去123456*1000对吧。下面上代码,结合上面的解释看哦 ~

#include <stdio.h>
#include <string.h>
#include <math.h>
#define ull unsigned long long
const int base=131;//此处p取131 
ull hash[1000010]={0},p[1000010];//拿p数组记住是怕用pow算p返回的值是long long型会丢失精度 
char a[1000010];
int main()
{
 long long i,L1,R1,L2,R2,n;
 p[0]=1;
 for(i=1;i<1000010;i++)p[i]=p[i-1]*base;//将P的次方存入P数组 
 while(~scanf("%s",a+1))  //从1开始 
 {
  scanf("%lld",&n);
  for(i=1;a[i];i++)
   hash[i]=hash[i-1]*base+(a[i]-'a'+1);//加1是因为如果a是第一位的话,第一位不能为0 
  while(n--)
  {
   scanf("%lld%lld%lld%lld",&L1,&R1,&L2,&R2);
   ull p1=hash[R1]-hash[L1-1]*p[R1-L1+1];//减去前面的数 
   ull p2=hash[R2]-hash[L2-1]*p[R2-L2+1];
   if(p1==p2)printf("Yes\n");
   else printf("No\n");
  }
 }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值