You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 841. 字符串哈希

一、题目描述

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l_1,r_1,l_2,r_2,请你判断 [l_1,r_1][l_2,r_2] 这两个区间所包含的字符串子串是否完全相同。

字符串中只包含大小写英文字母和数字。

输入格式 第一行包含整数 nm,表示字符串长度和询问次数。

第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。

接下来 m 行,每行包含四个整数 l_1,r_1,l_2,r_2,表示一次询问所涉及的两个区间。

注意,字符串的位置从 1 开始编号。

输出格式 对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No

每个结果占一行。

数据范围 1≤n,m≤10^5

输入样例:

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

输出样例:

Yes
No
Yes

二、算法思路

字符串哈希 O(n+m)

全称 字符串前缀哈希法,把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字。并且,用h[N]记录字符串前N个字符的Hash值,类似于前缀和。

作用就是把O(N)的时间复杂度降为O(1)。比如本题就是对比任意两段内字符串是不是相同,正常就是类似于一个循环长度次的substr,其实用hash差就能一步搞定!

举个栗子

str="ABCABCDEYXCACWING";
h[0]=0;
h[1]="A"Hash;
h[2]="AB"Hash;
h[3]="ABC"Hash;
h[4]="ABCA"Hash;

对形如X_1X_2X_3...X_{n-1}X_n的字符串,采用字符 ASCII码乘上P次方来计算哈希值。

映射公式:(X_1 \times P^{n-1} +X_2 \times P^{n-2}+...+X_{n-1}\times P^1 + X_n \times P^0) mod \ Q

比如:字符串ABCDP=131 那么h[4]=65*131^3+66*131^2+67*131^1+68*131^0

ABP=131 说是h[2]=65*131^1+66*131^0

我们想要求"CD"的HASH值,怎么求呢? 就是 h[4]-h[2]*131^2

注意

  1. 任意字符不可以映射成0,否则会出现不同的字符串都映射成0的情况,比如A,AA,AAA皆为0

  2. 冲突问题:通过巧妙设置P(13113331) ,Q(2^{64})的值,一般可以理解为不产生冲突(玄学)。

Q=2^{64}这个取模动作在代码中没有出现过,这是因为采用了unsinged \ long\ long ,它本身就是2^{64},如果超过了这个数字,就直接自动溢出,起到了取模的作用。

问题是比较不同区间的子串是否相同,就转化为对应的哈希值是否相同。

求一个字符串的哈希值就类似于构建一维前缀和,求一个字符串的子串哈希值就相当于一维前缀和应用:

构建: h[i]=h[i-1] \times P+s[i-1] \ \ \ i∈[1,n] h为前缀和数组,s[i-1]为字符串数组此位置字符对应的ASCII码。

应用: 查询l,r之间部分字符串的hash=h[r]h[l1]×P^{rl+1}

三、实现代码

#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ULL;

const int N = 100010;
const int P = 131;
int n, m;
string str;
ULL h[N], p[N];
ULL get(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main() {
    cin >> n >> m >> str;
    p[0] = 1; // p^0=1,初始化
    for (int i = 1; i <= n; i++) {
        p[i] = p[i - 1] * P;              // 基数
        h[i] = h[i - 1] * P + str[i - 1]; // 前缀和
    }
    while (m--) {
        int l1, r1, l2, r2;
        cin >> l1 >> r1 >> l2 >> r2;
        if (get(l1, r1) == get(l2, r2))
            puts("Yes");
        else
            puts("No");
    }
    return 0;
}