字符串Hash-

字符串Hash可以理解为,把一个字符串转换为一个整数。

如果我们通过某种方法,将字符串转换为一个整数,就可以便的确定某个字符串是否重复出现过。

Hash方法
给定一个字符串S=s1s2s3…sn S = s_1s_2s_3…s_nS=s
对字母x,我们规定idx(x)=x−′a′+1。
公式:
hash[i]=hash[i−1]∗p+id(s[i])。
单Hash方法:
公式: hash[i]=(hash[i−1])∗p+idx(s[i]) % mod

其中p pp和mod modmod均为质数,且有p<mod p < modp<mod。
对于此种Hash方法,将p和mod尽量取大即可,这种情况下,冲突的概率是很低的。
如取p=13,mod=101 p = 13, mod = 101p=13,mod=101,对字符串abc abcabc进行Hash
hash[0] = 1
hash[1] = (hash[0] * 13 + 2) % 101 = 15
hash[2] = (hash[1] * 13 + 3) % 101 = 97
双Hash:将一个字符串用不同的mod modmod hash两次,将这两个结果用一个二元组表示,作为Hash结果。
公式:hash1[i]=(hash1[i−1])∗p+idx(s[i]) % mod1

hash2[i]=(hash2[i−1])∗p+idx(s[i]) % mod2 hash2[i] = (hash2[i-1]) * p + idx(s[i]) \ % \ mod2hash2[i]=(hash2[i−1])∗p+idx(s[i]) % mod2

hash结果为<hash1[n],hash2[n]> <hash1[n],hash2[n]><hash1[n],hash2[n]>
这种Hash很安全的。
获取子串的Hash
如果我们求出一个串的Hash,就可以求解其子串的Hash值。
我们先以一个具体的例子来理解。
很久很久以前,森林里住着一群兔子。

有一天,兔子们想要研究自己的 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≤10000001≤length(S),m≤1000000
输入样例:
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
在这个例题中我们就需要去求一个字符串中子串的Hash的值。
我们可以运用公式:若已知一个∣S∣=n |S| = n∣S∣=n的字符串的hash值,hash[i],1≤i≤n hash[i], 1\le i \le nhash[i],1≤i≤n,其1≤l≤r≤n对应的hash值为:
hash=hash[r]−hash[l−1]∗p r−l+1;
对其子给的范围进行求hash的值,再进行比较即可。
实现代码如下:
#include<stdio.h>
#include<string.h>
typedef unsigned long long ull;
#define Hash 131
int main()
{
const int N=1e6+10;
ull p[N],sum[N];
int n,m,len,a,b,c,d,i,j;
char s[N];
scanf("%s",s);
len=strlen(s);
scanf("%d",&n);
p[0]=1;
for(i=1;i<=len;i++)
{
sum[i]=sum[i-1]*Hash+(s[i-1]-‘a’+1);
p[i]=p[i-1]*Hash;
}
for(j=1;j<=n;j++)
{
scanf("%d %d %d %d",&a,&b,&c,&d);
if(sum[b]-sum[a-1]==sum[d]-sum[c-1]*p[d-c+1])
{
printf(“Yes\n”);
}
else
printf(“NO\n”);
}
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值