acwing 841. 字符串哈希-java版本
题目所属分类
字符串哈希
想让h[l-1]向左移 与高位对齐的话 只需要乘上P的r-l+2次方就可以了
可以推导式子 然后错位相减就好了 h[r]-h[l-1]得出R到L的哈希值来
y总模板
核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果
typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
作者:yxc
链接:https://www.acwing.com/blog/content/404/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
原题链接
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2]
这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
代码案例:输入样例:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
题解
import java.util.Scanner;
public class Main{
static int N = 100003,P = 131 ;
static long[] h = new long[N];//这是存放hash前缀值得数组
static long[] p = new long[N];//这是存放p的n次方的数组
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
String arr = scan.next();
p[0] = 1 ;
for(int i = 1 ; i <= n ; i++){
p[i] = p[i-1] * P ;
h[i] = h[i-1] * P + arr.charAt(i - 1 );//因为从1 开始 而字符串读入是从0开始 所以是i-1
}
while(m-- > 0){
int l1 = scan.nextInt();
int r1 = scan.nextInt();
int l2 = scan.nextInt();
int r2 = scan.nextInt();
//判断两个区间是不是相同,用get的方法返回值一样说明区间的hash值是一样的
if(get(l1,r1) == get(l2,r2)) System.out.println("Yes");
else System.out.println("No");
}
}
static long get(int l , int r){
return h[r] - h[l-1]*p[r-l+1];
}
}
详细版 参考了别人
import java.util.Scanner ;
public class Main{
//开的是long类型数组,本来是需要进行前缀hash求完之后需要进行模2的64次方来防止相同的冲突,可能超过我们给的数组大小
static int N = 100010,P = 131;//p是进制数,惊艳值
static long[] h = new long[N];//这是存放hash前缀值得数组
static long[] p = new long[N];//这是存放p的n次方的数组
public static long get(int l,int r){//这里是将运用了一点前缀和公式的方式进行计算
//求l-r区间的hash值,就要用h[r] - h[l-1],因为两者位数不用需要让h[l-1]向左边移到跟h[r]对齐
//就比如求1234的3-4区间位,1234 - 12,12左移然后就让12*10^(4-3+1)=12*10^2=1200,然后1234-1200 = 34,这样进行计算出来
//然后本题是p进制,所以需要将上面公式中的10换成p就行了
//h[0] = 0
//h[1] = h[i-1] * P + str[1] = 0*P+a = a
//h[2] = a * P + b
//h[3] = (a*P+b)*P+c = a*p[2]+b*P+c
//h[4] = (a*p[2]+b*P+c)*P+d = a*p[3]+b*p[2]+c*P+d
//比如abcd求3-4区间位,就是让h[d]-h[b],h[b]位数不用需要向左移对齐h[d],
//h[2]*P^(4-3+1)=(a*P+b)*P^2 = a*P^3+b*P^2
//然后就让h[d] - h[b]求出34区间值,(a*p[3]+b*p[2]+c*P+d) - (a*P^3+b*P^2) = c*P+d
return h[r] - h[l-1]*p[r-l+1];
}
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
String s = scan.next();
p[0] = 1;//这个是p的0次方的值,需要单独写出来,非常重要
for(int i = 1 ; i <= n ; i++ ){
p[i] = p[i-1] * P;//这里对应每一个下标对应对应P的多少次方
h[i] = h[i-1] * P + s.charAt(i-1);//这里是公式,预处理前缀哈希的值,因为是P进制,所以中间乘的是P
}
while(m -- > 0){
int l1 = scan.nextInt();
int r1 = scan.nextInt();
int l2 = scan.nextInt();
int r2 = scan.nextInt();
//判断两个区间是不是相同,用get的方法返回值一样说明区间的hash值是一样的
if(get(l1,r1) == get(l2,r2)) System.out.println("Yes");
else System.out.println("No");
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)