一、题目
1、题目描述
给定整数 p p p 和 m m m ,一个长度为 k k k 且下标从 0 0 0 开始的字符串 s s s 的哈希值按照如下函数计算:
h a s h ( s , p , m ) = ( v a l ( s [ 0 ] ) ∗ p 0 + v a l ( s [ 1 ] ) ∗ p 1 + . . . + v a l ( s [ k − 1 ] ) ∗ p k − 1 ) m o d m hash(s, p, m) = (val(s[0]) * p^0 + val(s[1]) * p^1 + ... + val(s[k-1]) * p^{k-1}) \mod m hash(s,p,m)=(val(s[0])∗p0+val(s[1])∗p1+...+val(s[k−1])∗pk−1)modm
其中 v a l ( s [ i ] ) val(s[i]) val(s[i]) 表示 s [ i ] s[i] s[i] 在字母表中的下标,从 v a l ( ′ a ′ ) = 1 val('a') = 1 val(′a′)=1 到 v a l ( ′ z ′ ) = 26 val('z') = 26 val(′z′)=26 。给你一个字符串 s s s 和整数 p o w e r power power, m o d u l o modulo modulo, k k k 和 h a s h V a l u e hashValue hashValue。请你返回 s s s 中 第一个 长度为 k k k 的 子串 s u b sub sub ,满足hash(sub, power, modulo) == hashValue
。测试数据保证一定 存在 至少一个这样的子串。
样例输入:nums = [0,0,1,0]
样例输出:[2,4]
2、基础框架
- C语言 版本给出的基础框架代码如下:
char * subStrHash(char * s, int power, int modulo, int k, int hashValue){
}
3、原题链接
二、解题报告
1、思路分析
(
1
)
(1)
(1) 把
v
a
l
(
s
[
i
]
)
val(s[i])
val(s[i])化简为
g
(
i
)
g(i)
g(i)
(
2
)
(2)
(2) 对于长度为 k
的字符串
s
[
0
:
k
−
1
]
s[0:k-1]
s[0:k−1] 的哈希值是:
h
(
0
,
k
−
1
)
=
(
g
(
0
)
p
0
+
g
(
1
)
p
1
+
.
.
.
+
g
(
k
−
1
)
p
k
−
1
)
m
o
d
m
h(0, k-1) = (g(0) p^0 + g(1) p^1 + ... + g(k-1) p^{k-1}) \ mod \ m
h(0,k−1)=(g(0)p0+g(1)p1+...+g(k−1)pk−1) mod m
(
3
)
(3)
(3) 对于一个从
i
i
i 开始,长度为
k
k
k 的字符串
s
[
i
:
i
+
k
−
1
]
s[i:i+k-1]
s[i:i+k−1],它的哈希值等于:
h
(
i
,
i
+
k
−
1
)
=
(
g
(
i
)
p
0
+
.
.
.
+
g
(
i
+
k
−
1
)
p
k
−
1
)
m
o
d
m
h(i, i+k-1) = (g(i) p^0 + ... + g(i+k-1) p^{k-1}) \ mod \ m
h(i,i+k−1)=(g(i)p0+...+g(i+k−1)pk−1) mod m
(
4
)
(4)
(4) 则从
i
+
1
i+1
i+1 开始,长度为
k
k
k 的字符串
s
[
i
+
1
:
i
+
k
]
s[i+1:i+k]
s[i+1:i+k],它的哈希值等于:
h
(
i
+
1
,
i
+
k
)
=
(
g
(
i
+
1
)
p
0
+
.
.
.
+
g
(
i
+
k
)
p
k
−
1
)
m
o
d
m
h(i+1, i+k) = (g(i+1) p^0 + ... + g(i+k) p^{k-1}) \ mod \ m
h(i+1,i+k)=(g(i+1)p0+...+g(i+k)pk−1) mod m
(
5
)
(5)
(5) 对相邻两个字符串的哈希值进行处理,得到:
h
(
i
+
1
,
i
+
k
)
p
−
h
(
i
,
i
+
k
−
1
)
=
g
(
i
+
k
)
p
k
−
g
(
i
)
h(i+1, i+k)p - h(i, i+k-1) = g(i+k) p^{k} - g(i)
h(i+1,i+k)p−h(i,i+k−1)=g(i+k)pk−g(i)
(
6
)
(6)
(6) 移项(仔细仔细),得到:
h
(
i
,
i
+
k
−
1
)
=
h
(
i
+
1
,
i
+
k
)
p
−
g
(
i
+
k
)
p
k
+
g
(
i
)
h(i, i+k-1) = h(i+1, i+k)p - g(i+k) p^{k} + g(i)
h(i,i+k−1)=h(i+1,i+k)p−g(i+k)pk+g(i)
(
7
)
(7)
(7) 转化成后缀和求解,对于长度为
n
n
n 的字符串,初始情况就是
h
(
n
−
k
,
n
−
1
)
h(n-k, n-1)
h(n−k,n−1)
2、时间复杂度
O ( n ) O(n) O(n)。
3、代码详解
int g(char c) {
return (c - 'a' + 1);
}
char * subStrHash(char * s, int power, int modulo, int k, int hashValue){
int i;
int n = strlen(s);
int idx;
long long sum = 0;
long long p[100005] = {1};
char *ret = (char *)malloc( sizeof(char) * 100005 );
int size = 0;
for(i = 1; i <= k; ++i) {
p[i] = p[i-1] * power % modulo;
}
// 先求 h(n-k, n-1)
for(i = n-k; i < n; ++i) {
sum += p[i-n+k] * g(s[i]);
sum %= modulo;
if(sum == hashValue) {
idx = n-k;
}
}
// h(i, i+k-1) = h(i+1, i+k)p - g(i+k) p^{k} + g(i)
for(i = n-k-1; i >= 0; --i) {
sum = sum * p[1] - p[k] * g(s[i+k]) + g(s[i]);
sum = (sum % modulo + modulo) % modulo;
// printf("sum = %lld\n", sum);
if(sum == hashValue) {
idx = i;
}
}
for(i = idx; i < idx + k; ++i) {
ret[size++] = s[i];
}
ret[size] = '\0';
return ret;
}
三、本题小知识
利用相邻字符串哈希值可以递推计算,减少每个子串哈希值计算的时间复杂度。
四、加群须知
相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」。
那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:
如果链接被屏蔽,或者有权限问题,可以私聊作者解决。
大致题集一览:
为了让这件事情变得有趣,以及「 照顾初学者 」,目前题目只开放最简单的算法 「 枚举系列 」 (包括:线性枚举、双指针、前缀和、二分枚举、三分枚举),当有 一半成员刷完 「 枚举系列 」 的所有题以后,会开放下个章节,等这套题全部刷完,你还在群里,那么你就会成为「 夜深人静写算法 」专家团 的一员。
不要小看这个专家团,三年之后,你将会是别人 望尘莫及 的存在。如果要加入,可以联系我,考虑到大家都是学生, 没有「 主要经济来源 」,在你成为神的路上,「 不会索取任何 」。
🔥联系作者,或者扫作者主页二维码加群,加入刷题行列吧🔥
🔥让天下没有难学的算法🔥
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
让你养成九天持续刷题的习惯 🔥《九日集训》🔥
入门级C语言真题汇总 🧡《C语言入门100例》🧡
组团学习,抱团生长 🌌《算法零基础100讲》🌌
几张动图学会一种数据结构 🌳《画解数据结构》🌳
竞赛选手金典图文教程 💜《夜深人静写算法》💜