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");
}
}
}