字符串哈希

字符串哈希

题目

给定一个长度为 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;
    }
}
posted @ 2022-08-09 20:59  INnoVation-V2  阅读(21)  评论(0编辑  收藏  举报