字符串哈希
题目
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 \(l_1,r_1,l_2,r_2\),请你判断 \([l_1,r_1]\) 和 \([l_2,r_2]\) 这两个区间所包含的字符串子串是否完全相同。字符串中只包含大小写英文字母和数字。
思路
利用哈希的思想,将一段字符串通过哈希函数转换为一串数字,然后通过对比两个数字是否相同,判断两个字符串是否相等。
具体做法
1.求字符串的哈希值
以ABCD为例,将其视为一个P进制数,则
$ Hash(ABCD) = A \times p^4 + B \times p^3 + C \times p^2 + D \times p^1 $
此时,Hash值必然是一个较大的数,可能会溢出,因此需要对Q取余,即
Hash(ABCD) = Hash(ABCD) % Q
这里存在两个经验值
令P = 131 或 13331,Q = 2^64时,冲突的概率会较小
2.如何求出其中任意一段的哈希值
假设已知Hash(ABCDE),如何求出Hash[3,5],即Hash[CDE]的值?
$ Hash[CDE] = C \times P^3 + D \times P ^ 2 + E \times p^1 $
$ Hash[ABCDE] = A \times P^5 + B \times p^4 + C \times p^3 + D \times p^2 + E \times p^1 $
$ Hash[AB] = A \times P^2 + D \times P^1 $
因此
\(Hash[CDE] = Hash[ABCDE] - Hash[AB] \times P^3 \\ = Hash[ABCDE] - Hash[AB] \times P^{3-1+1}\)
代码
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
//ULL为64位无符号整数,使用它进行存储,相当于对2^64取余
typedef unsigned long long ULL;
int P = 131;
char s[N];
ULL h[N], p[N];
int query(int l, int r){
return h[r] - h[l - 1] * p[r - l + 1];
}
int main(){
int n, m;
cin >> n >> m >> (s + 1);
p[0] = 1;
for(int x = 1; x <= n; x++){ //求哈希值
h[x] = h[x - 1] * P + s[x];
p[x] = p[x - 1] * P;
}
while(m--){ //查询
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
cout << (query(l1, r1) == query(l2, r2) ? "Yes" : "No") << endl;
}
}