摘要:
字符串前缀哈希法。
题目
给定一个长度为 $n$ 的字符串,再给定 $m$ 个询问,每个询问包含四个整数 $l1,r1,l2,r2$,请你判断 $[l1,r1]$ 和 $[l2,r2]$ 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 $n$ 和 $m$,表示字符串长度和询问次数。
第二行包含一个长度为 $n$ 的字符串,字符串中只包含大小写英文字母和数字。
接下来 $m$ 行,每行包含四个整数 $l1,r1,l2,r2$,表示一次询问所涉及的两个区间。
注意,字符串的位置从 $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
代码
1 |
|
1 |
理解
字符串前缀哈希法,本质就是不把字符串当字符串看待,而是把字符串看成一个 $P$ 进制的数。
假设有字符串 12345678
, 我们把它看做一个 $P = 10$ 进制的数。
现在我们规定一个示例哈希算法为:每个字符的哈希值为当前字符减 1
这个字符得到的结果。
那么在整个字符串中,位于 $1$ 到 $8$ 区间内的字符串哈希值 $Hash(1, 8) = 12345678$.
下面我们研究子串的哈希关系。
位于 $1$ 到 $6$ 区间内的字符串哈希值 $Hash(1, 6) = 123456$.
显然位于 $7$ 到 $8$ 区间内的字符串哈希值:
$$Hash(7, 8) = 78 = 12345678 - 12345600 = 12345678 - 123456 \times 10^2$$
$$=$$
$$Hash(1, 8) - Hash(1, 6) \times 10^{8 - 7 + 1}$$
有结论:
对于一个 $P$ 进制字符串,它的 $l$ 到 $r$ 下标之间的子串哈希值满足如下公式:
$$Hash(l, r) = Hash(1, r) - Hash(1, l - 1) \times P^{r - l + 1}$$
在本题中,预处理前缀哈希数组 $h$,$h[i]$ 表示位于 $1$ 到 $i$ 之间的字符串哈希值。
根据上述结论有:
$$Hash(i, i) = Hash(1, i) - Hash(1, i - 1) \times P^{1}$$
其中 $Hash(i, i)$ 为第 $i$ 个字符的哈希值。
即 $h[i] = Hash(i, i) + h[i - 1] \times P$.
小技巧
根据经验,本题 $P$ 值 可以取值 $131$ 或 $13331$。
如果字符串超长,那么哈希得到的值会溢出,需要取模,我们使用
unsigned long long
溢出特性来自动对该类型最大值取模。
原题链接: AcWing 841. 字符串哈希