目录
1.引入问题
题目描述:
给定一个长度为 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
问题分析:
问题的关键在于如何快速判断两个字符串是否相等
我们的做法是:将字符串一一映射为数字,两个数字相等,等价于字符串相等。此时一次查询的时间复杂度为O(1)。
2. 字符串哈希的步骤
整体思路:
将字符串转换成一个P进制的数字,相同的字符对应的P进制数字一定相同,即哈希值相同。
故求出s[l1, r1] 和 s[l2,r2]对应的字符串的哈希值h[s[l1, r1]] h[s[l2, r2]],并判断是否相等。就能得出l1, r1 l2,r2对应的字符串是否相同
2.1预处理字符串前缀的哈希值
把字符串变成一个p进制数字
对形如 X1 X2 X3 ⋯Xn−1 Xn的字符串,采用字符的ascii 码乘上 P 的次方来计算哈希值。
映射公式
(X1×Pn−1+X2×Pn−2+⋯+Xn−1×P1+Xn×P0)modQ
说明:
1. modQ是为了防溢出,Q 一般为2^64
2.冲突问题:通过巧妙设置P (131 或 13331) 的值,一般可以理解为不产生冲突。
举例1:
s为数字字符串,各个位置的值等于数字本身,P等于10
s = "4321"
has[1] = s[1] = 4;
has[2] = has[1]*p + s[2] = 4*10+3 = 43;
has[3] = has[2]*p + s[3] = 43*10+2 = 432;
has[4] = has[3]*p + s[4] =432*10 + 1 =4321;
举例1是为了我们更好的理解举例2(采用字符的ascii 码乘上 P 的次方来计算哈希值)
举例2:
s = "abca",各个位置的值等于ascii本身,P等于131
has[1] = s[1] = 97 (a);
has[2] = has[1]*p + s[2] = 97 *131 + 98 = 12,805 (ab);
has[3] = has[2]*p + s[3] = 12,805 * 131 + 99 =1,677,554 (abc);
has[4] = has[3]*p + s[4] = 1,677,554 * 131 + 97 = 219,759,671 (abca);
小结:
预处理字符串前缀的哈希值的模板:
for (int i = 1; i <= n; i++) {
has[i] = has[i - 1] * p + str.charAt(i - 1);
}
2.2获取字符串中的任意子串的哈希值
思想:前缀和思想
引用举例1:s为数字字符串,各个位置的值等于数字本身,P等于10
s = "4321"
求:has[s[3,4]] 意思是求"21"的哈希值
has[s[3,4]] = has[4] - has[2]*p^2 = 4321 - 43*100 = 21;
引用举例2:s = "abca",各个位置的值等于ascii本身,P等于131
求:has[s[3,4]] 意思是求"ca"的哈希值
has[s[3,4]] = has[4] - has[2]*p^2
小结:
获取字符串中的任意子串的哈希值的模板:
private static long get_has(int l, int r) {
return has[r] - has[l - 1] * mul[r - l + 1];
}
其中mul[r - l + 1]为P^(r-l+1);
故也要提前预处理,可以在 预处理字符串前缀的哈希值的时候一起处理。
3.代码
import java.util.*;
public class Main {
// 第一行包含整数 n和 m,表示字符串长度和询问次数。
// 第二行包含一个长度为 n的字符串,字符串中只包含大小写英文字母和数字。
// 接下来 m行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。
// 注意,字符串的位置从 1开始编号。
static int N = 100010, p = 131;
static long[] has = new long[N];
static long[] mul = new long[N];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
String s = in.next();
mul[0] = 1;//P^0=1
//预处理字符串前缀的哈希值
for (int i = 1; i <= n; i++) {
has[i] = has[i - 1] * p + s.charAt(i - 1);
mul[i] = mul[i - 1] * p;
}
while (m-- > 0) {
int l1 = in.nextInt();
int r1 = in.nextInt();
int l2 = in.nextInt();
int r2 = in.nextInt();
//获取字符串中的任意子串的哈希值
if (get_has(l1, r1) == get_has(l2, r2)) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
private static long get_has(int l, int r) {
return has[r] - has[l - 1] * mul[r - l + 1];
}
}
以上为我个人的小分享,如有问题,欢迎讨论!!!
都看到这了,不如关注一下,给个免费的赞