C语言算法入门【码蹄集进阶塔】MT2172 萨卡兹人——字符串哈希

C语言算法入门【码蹄集进阶塔】MT2172 萨卡兹人——字符串哈希

题目描述

很久很久以前,卡西米尔里住着萨卡兹人,他们彼此争斗不休。有一天,他们想要研究自己的 DNA序列,来证明他们是一个种群。我们首先选取一个好长好长的序列(DNA序列包含26个小写英文字母),然后我们每次选择两个区间,这两个区间代表两个萨卡兹人的DNA序列,这两个萨卡兹一模一样的唯一可能是他们的DNA序列一模一样。
输入格式:
第一行一个DNA字符串S。
接下来一个数字m,表示m次询问。
接下来 m行,每行四个数字l1,r1,l2,r2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。
.
输出格式: 对于每次询问,输出一行表示结果。如果两个萨卡兹完全相同输出Yes,否则输出No

样例

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

备注:

提示
1.1< l1 ≤ r1 ≤ length(S),1 ≤ l2 ≤ r2 ≤ length(S)
2.1 ≤ length(S),m ≤ 1000000
MT2172这道题主要运用字符串哈希的知识
字符串哈希:把不同的字符串映射成不同的整数,来判断它们是不是相等。

字符串哈希的核心思想是将字符串内容转换为一个整数,使得具有相同内容的字符串产生相同的哈希值,而不同的字符串产生不同的哈希值。来进行

  • 字符串匹配:快速比较两个子串是否相等。
  • 模式查找:在文本中查找一个特定的模式字符串。

在这里插入图片描述

但是在有些情况下,两个字符串不一样,但是计算出来的哈希值是一样的,这种情况就叫哈希碰撞。

为了减少这种情况发生,通常会使用一个大的模数M来减少冲突的概率,并且巧妙的设置P和M的值。

通常情况下要保持P和M的互质,P=131或13131,M=2^64。
(2^64为ULL类型的最大值)
关于为什么要这么取值,感兴趣的同学可以自己查阅资料,这里就不展开说了。

哈希碰撞(Hash Collision):是指不同的输入数据产生了相同的哈希值。
ULL:Unsiged Long Long(无符号长整形)

然后我们需要求中间的字符串的哈希值只需要两个哈希值相减就可以了

在这里插入图片描述

到此,这个题的核心思想我们就讲完了,以下是代码和核心代码块的解释。

参考代码

#include <stdio.h>
#include <string.h>

#define ULL unsigned long long
#define P 131
#define N 1000007

ULL p[N], h[N]; // p[i] = P^i, h[i] = s[1~i]
char s[N];

int m, l1, r1, l2, r2;

void init(int n) {
    int i;
    p[0] = 1, h[0] = 0;
    for(i = 1; i <= n; ++i) {
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + s[i];
    }
}

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

int substr(int l1, int r1, int l2, int r2) {
    return get(l1, r1) == get(l2, r2);
}

int main() {
    scanf("%s", s + 1); // 因为索引从1开始
    scanf("%d", &m);
    int len = strlen(s + 1);
    init(len);
    while(m--) {
        scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
        if (substr(l1, r1, l2, r2))
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

函数原型声明

init 函数
void init(int n) {
    int i;
    p[0] = 1, h[0] = 0;
    for(i = 1; i <= n; ++i) {
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + s[i];
    }
}
  • init 函数用于初始化哈希值。它计算 p 数组中的幂次和 h 数组中的前缀哈希值。
  • p[0] = 1h[0] = 0 是初始化条件。
  • 循环计算每个位置的哈希值。
get 函数
ULL get(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}
  • get 函数用于计算子串 s[l~r] 的哈希值。
  • 它通过减去前缀哈希值和乘以相应的幂次来得到子串的哈希值。
substr 函数
int substr(int l1, int r1, int l2, int r2) {
    return get(l1, r1) == get(l2, r2);
}
  • substr 函数比较两个子串的哈希值是否相等。
  • 如果相等,返回 1(真),否则返回 0(假)。

结语:

写这篇博客旨在记录我自己在学习算法的路上遇到的经典或者有趣的算法,并将它们分享给大家,以后可能还会持续更新,如果有觉得写的不错同学可以赏个赞再走哦。
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值