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] = 1
和h[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(假)。