ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

13 | 字符串哈希(字符串的哈希判等、前缀和的思想)

2022-06-16 09:01:49  阅读:126  来源: 互联网

标签:子串 13 int 26 long 哈希 字符串


题目描述

给定一个含有 26 个小写英文字母的字符串。有m次询问,每次给出2个区间,请问这两个区间里的子字符串是否一样?

输入

第一行输入一个字符串 S。
第二行一个数字 m,表示 m 次询问。
接下来 m 行,每行四个数字 l1,r1,l2,r2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。
数据范围:
1≤length(S),m,l1,r1,l2,r2≤1000000。

输出

对于每次询问,输出一行表示结果。
如果两个子串完全一样,输出 Yes,否则输出 No(注意大小写)。

样例

输入

aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2

输出

Yes
No
Yes

字符串哈希

什么是哈希

哈希算法是:通过哈希函数将字符串、较大的数等转换为能够用变量表示的或者是直接能作为数组下标的数,通过哈希算法转换到的值,称之为哈希值。哈希值可以实现快速查找和匹配。
比如:用数组下标计数法,统计一个字符串中,每种字母出现的次数就是一个简单的哈希,将每个字母都映射为了对应的ascii码。

如何构造哈希

原理:将字符串中的每一个字母都看作是一个数字(例:从a-z,视为1一26);将字符串视为是一个b进制的数。(注意,不能映射为0,因为如果a为0,那么a、aa、aaa的值将都为0)

比如:可以将字符串s="abcd"视为26进制的整数,则可以计算出:

hash(s):1 * 26^3+2 * 26^2+ 3 * 26^1+4* 26^0。如果字符串很长,h(s)很容易超出long long的范畴,为防止溢出,我们取一个固定的值h用hash(s)%h,使得结果在long long范围内。![img](file:///C:/Users/克特/AppData/Local/Temp/msohtmlclip1/01/clip_image001.jpg)

选取两个合适的互质常数 b和h (b<h),其中h要尽可能的大一点,为了降低冲突(不同字符串计算到同一个哈希值)的概率。

一般来说: b取 131 或13331, h取26^4,最终产生的哈希值的冲突的概率极低。

image-20220615225254948

滚动哈希优化

如果针对一个很长的字符串,判断其中两个长度为len的子串是否相同,如果采用0(len)的时间复杂度计算出对应的子串hash,那和直接取出子串比较的时间复杂度并无差异,因此我们需要使用滚动哈希优化的技巧,可以在0(1)的时间复杂度下取出子串的hash值。

滚动计算到前缀哈希 h(K)

设:h(k)为字符串C前k个字符构成的子串的哈希值,(先不考虑取模):
image-20220615225521879
类比10进制理解该公式,比如10进制的12345,取出前3个数是123,如果要取前4个数,可以使用: 123 * 10(进制) + 4(Ck+1) = 1234 的方法来取出。

利用前缀哈希 H(k) 计算区间哈希值

image-20220615225622115

由.上述公式可知,只需要预处理出b",就可以在0(1)的时间内求得任意子串的哈希值。

时间复杂度

综上,如果在一个长度为n的字符串中,任意取长度为m的子串进行匹配,时间复杂度
为0(n+m)。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ULL;
const int N=1e6+10,P=131;
//h[i]:代表 1~i 区间中字串的哈希值
//p[i]:代表P的i次方
ULL h[N],p[N];
char s[N];
int m;
//返回字符串子区间的哈希值
ULL get(int l,int r){
	return h[r]-h[l-1]*p[r-l+1];
}
int main(){
	scanf("%s",s+1);
	//计算出 h[] 和 p[] 的值
	p[0]=1;
	int len = strlen(s+1);
	for(int i=1;i<=len;i++){
		p[i]=p[i-1]*P;
		h[i]=h[i-1]*P+(s[i]-'a'+1);
	}
	scanf("%d",&m);
	int l1,r1,l2,r2;
	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;
}

标签:子串,13,int,26,long,哈希,字符串
来源: https://www.cnblogs.com/mmxingye/p/16380665.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有