AcWing 138 兔子与兔子

题目描述:

很久很久以前,森林里住着一群兔子。有一天,兔子们想要研究自己的 DNA 序列。

我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。

输入格式

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

输出格式

对于每次询问,输出一行表示结果。如果两只兔子完全相同输出 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是指将一个任意长度的字符串映射为一个非负整数,并且冲突的概率几乎为0.

一般而言,我们将字符串看作P进制数,将每个字符都视为一个大于0的数值。比如,对于由小写字母构成的字符串可以令a = 1,b = 2,...,z = 26.取固定值M,求出该P进制数对M的余数即为该字符串的Hash值。一般而言,我们取P = 131或13331,此时冲突概率会很低,所以一般认为,Hash值相等,就可以认为字符串相等。为了避免取模运算,M一般取2^64,恰好是unsigned long long类型的最大表示范围,在计算时不用特殊处理溢出,溢出时就相当于自动取模了。

如何在O(n)的时间内构建出字符串的哈希映射呢?例如abc,a = 1,ab = a * 131 + b = 131 + 2,abc = ab * 131 + c。由此很容易可以推出哈希数组h[i] = h[i - 1] * base + str[i] - 'a' + 1。既然h[i]表示前i个字符的hash值,那么如何求得i 到 j字符的hash值呢?与之前推导类似,h[i - 1] * p[j - i + 1]加上i到j之间的hash值即可得到h[j]的哈希值,所以i到j之间的hash值为h[j] - h[i - 1] * p[j - i + 1],为此,我们需要在求hash值的前缀和时对各个位置的权重也用p[i]存储一下,避免使用时再去计算。

于是,构建好hash表后,本题就可以在O(1)的时间内得到l到r范围内字符串的hash值并与另一子串的hash值对比判断是否相等了。

#include <iostream>
#include <cstring>
using namespace std;
typedef unsigned long long ull;
const int maxn = 1000010,base = 131;
char str[maxn];
ull h[maxn],p[maxn];
ull get(int l,int r){
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main(){
    scanf("%s",str + 1);
    int n = strlen(str + 1);
    p[0] = 1;
    for(int i = 1;i <= n;i++){
        h[i] = h[i - 1] * base + str[i] - 'a' + 1;
        p[i] = p[i - 1] * base;
    }
    int m;
    cin>>m;
    while(m--){
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        if(get(l1,r1) == get(l2,r2))    puts("Yes");
        else    puts("No");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值