AcWing: 138.兔子与兔子 和 字符串hash的介绍

AcWing: 138. 兔子与兔子

目录

题目描述

算法介绍

解题思路

代码及注释


制作不易,点个赞再走呗

题目描述

很久很久以前,森林里住着一群兔子。

有一天,兔子们想要研究自己的 DNA 序列。

我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。

然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。

注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。

输入格式

第一行输入一个 DNA 字符串 S。

第二行一个数字 m,表示 m 次询问。

接下来 m 行,每行四个数字 l1,r1,l2,r2,分别表示此次询问的两个区间,注意字符串的位置从 11 开始编号。

输出格式

对于每次询问,输出一行表示结果。

如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)。

数据范围

1≤length(S),m≤1000000

输入样例:
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes

算法介绍

Hash(哈希)介绍

该题用到的算法是哈希;

简单来说就是把任意输入通过特定方式(hash函数) 处理后 生成一个值。这个值等同于存放数据的地址,这个地址里面再把输入的数据进行存储。
这个hash函数又叫散列函数,会有一些常用的构造散列函数的方法,但是处理结果值可能相同,那就叫冲突,冲突也有常用的冲突常用的冲突解决方法。

哈希的要点

哈希最重要的就是怎么得到哈希值,一般直接哈希得到的值会很大,所以一般要取模,为了使取模后精度比较高,一般使用unsigned long long 来存数据,而进行哈希使要乘的值也是有讲究的,经过人们的多次实验,一般取131,1331.....这样的精度比较高。

方法:

ULL p[N],ans[N];
int P = 131;
for(int i = 1; i <= len; i++)
	{
		ans[i] = ans[i-1] * P + str[i-1];
		p[i] = p[i-1] * P;
	}

当然,获取哈希值的方法不止这一种,要理解哈希的原理,就是将一个数值映射到另一个数进行储存(也可以是将一个字符或者是字符串映射到一个值上),这个获得的值的要求根据题意来判断。

解题思路

该题刚开始想可以直接用substr来获取两段的字符串直接判断,但是,根据查询资料得知,substr获取字符串的时间复杂度是线性的,最差可以是n,那么时间复杂度最高是n*n;对于该题来说时间复杂度太高了,所以不能直接用substr来写。

那么再想想可以用字符串哈希来写,将这个字符串的每一个前缀都用一个数值来表示,然后进行数值与数值之间的对比,这样时间复杂度就降下来了。

假设成立,下面就开始执行,那么该如何执行呢:

 我们可以将每一个前缀字符串都用哈希的方式进行了储存,然后就是截取了。

 那么要怎么处理呢:

ULL get(int l,int r)
{
	return ans[r] - ans[l-1] * p[r-l+1];
}

这样就获取该区域的哈希值,将两个区域的哈希值进行对比,然后进行判断就得出最后的答案。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

typedef unsigned long long ULL;

const int N = 1000005,P = 131;
char str[N];
ULL p[N],ans[N];

ULL get(int l,int r)
{
	return ans[r] - ans[l-1] * p[r-l+1];
}

int main (void)
{
	scanf("%s",str);
	int len = strlen(str);
	p[0] = 1;
	for(int i = 1; i <= len; i++)
	{
		ans[i] = ans[i-1] * P + str[i-1];
		p[i] = p[i-1] * P;
	}
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i++)
	{
		int l1,r1,l2,r2;
		scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
		if(get(l1,r1) == get(l2,r2))
		{
			printf("Yes\n");
		}
		else puts("No");
	}
	
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值