Part2.1 字符串算法-哈希和哈希表
字符串哈希算法
字符串的一般哈希函数为 f ( s ) = ∑ i = 1 ∣ s ∣ s i × b i m o d M f(s)=\sum_{i=1}^{|s|}s_i \times b^{i} \bmod M f(s)=∑i=1∣s∣si×bimodM
显然上述函数的求解时 O ( n ) O(n) O(n) 的,并且实现效率低下
若要多次询问一个字符串 s s s 的子串哈希值,该算法显然无法满足要求,我们期望能够在 O ( 1 ) O(1) O(1) 时间算出 f ( s l . . . r ) = ∑ i = 0 r − l s i + l × b i m o d M f(s_{l...r})=\sum_{i=0}^{r-l}s_{i+l} \times b^{i} \bmod M f(sl...r)=∑i=0r−lsi+l×bimodM 的具体数值
设 h ( s 0... i ) = ∑ j = 0 i s j × b j m o d M h(s_{0...i})=\sum_{j=0}^{i}s_j \times b^{j} \bmod M h(s0...i)=∑j=0isj×bjmodM
那么有:
h
(
s
0...
r
)
=
∑
i
=
0
r
s
i
×
b
i
m
o
d
M
h(s_{0...r})=\sum_{i=0}^{r}s_i \times b^{i} \bmod M
h(s0...r)=∑i=0rsi×bimodM
h
(
s
0...
l
−
1
)
=
∑
i
=
0
l
−
1
s
i
×
b
i
m
o
d
M
h(s_{0...l -1})=\sum_{i=0}^{l -1}s_i \times b^{i} \bmod M
h(s0...l−1)=∑i=0l−1si×bimodM
h
(
s
l
.
.
.
r
)
=
∑
i
=
l
r
s
i
×
b
i
m
o
d
M
=
h
(
s
1...
r
)
−
h
(
s
0...
l
−
1
)
=
f
(
s
l
.
.
.
r
)
×
b
l
m
o
d
M
h(s_{l...r})=\sum_{i=l}^{r}s_i \times b^{i} \bmod M=h(s_{1...r})-h(s_{0...l -1})=f(s_{l...r}) \times b^{l} \bmod M
h(sl...r)=∑i=lrsi×bimodM=h(s1...r)−h(s0...l−1)=f(sl...r)×blmodM
故我们可以依据前缀和 O ( 1 ) O(1) O(1) 算出 f ( s l . . . r ) f(s_{l...r}) f(sl...r)
但是由于计算中进行了模数运算,为了避免除法的出现,我们一般将函数 f f f 乘上 b N b^{N} bN,即 f ( s l . . . r ) = ∑ i = 0 r − l s i × b N + i m o d M f(s_{l...r})=\sum_{i=0}^{r-l}s_i \times b^{N+i} \bmod M f(sl...r)=∑i=0r−lsi×bN+imodM
那么则有 f ( s l . . . r ) = ( h ( s 1... r ) − h ( s 1... l − 1 ) ) × b N − l f(s_{l...r})=(h(s_{1...r})-h(s_{1...l -1})) \times b^{N-l} f(sl...r)=(h(s1...r)−h(s1...l−1))×bN−l
A. Oulipo
问题描述
给定字符串 A A A 和 B B B,询问 B B B 在 A A A 中出现了几次
解题思路
预处理 A A A 和 B B B 的哈希值,由于 B B B 串长度固定,枚举起点, O ( 1 ) O(1) O(1) 判断 A [ i . . . i + ∣ B ∣ − 1 ] A[i...i+|B|-1] A[i...i+∣B∣−1] 和 B B B 的哈希值是否相等即可
时间复杂度 O ( n ) O(n) O(n)
D. Seek the Name, Seek the Fame
问题描述
给定若干字符串,在每个字符串中求出所有既是前缀又是后缀的子串长度
解题思路
枚举前缀长度,判断前后缀哈希值是否一致即可
时间复杂度 O ( n ) O(n) O(n)
E. Friends
问题描述
给定一个字符串 S S S,先将字符串 S S S 复制一次,得到字符串 T T T,然后在 T T T 中插入一个字符,得到字符串 U U U,给出字符串 U U U,重新构造出字符串 S S S
解题思路
显然 ∣ U ∣ |U| ∣U∣ 必须为奇数
设要添加字符的位置为 x x x,不妨设 x x x 在串 U U U 的前半段
则 f ( S ) = ∑ i = 0 ∣ U ∣ 2 − 1 U ∣ U ∣ 2 + i + 1 × b N + i m o d M f(S) = \sum_{i=0}^{\frac{|U|}{2}-1}U_{\frac{|U|}{2}+i+1} \times b^{N+i} \bmod M f(S)=∑i=02∣U∣−1U2∣U∣+i+1×bN+imodM
f ( U 0... x − 1 ) = ∑ i = 0 x − 1 U i × b N + i m o d M f(U_{0...x-1})=\sum_{i=0}^{x-1}U_{i} \times b^{N+i} \bmod M f(U0...x−1)=∑i=0x−1Ui×bN+imodM
f ( U x + 1... ∣ U ∣ 2 ) = ∑ i = x + 1 ∣ U ∣ 2 U i × b N + i − x − 1 m o d M f(U_{x+1...\frac{|U|}{2}})=\sum_{i=x+1}^{\frac{|U|}{2}}U_{i} \times b^{N+i-x-1} \bmod M f(Ux+1...2∣U∣)=∑i=x+12∣U∣Ui×bN+i−x−1modM
易得 f ( U 0... x − 1 ) + f ( U x + 1... ∣ U ∣ 2 ) × b x + 1 = f ( S ) f(U_{0...x-1}) + f(U_{x+1...\frac{|U|}{2}}) \times b^{x+1} = f(S) f(U0...x−1)+f(Ux+1...2∣U∣)×bx+1=f(S)
x x x 在 U U U 后半段时亦然
故枚举 x x x, O ( 1 ) O(1) O(1) 判断左右两边 H a s h Hash Hash 值是否相等即可
总时间复杂度 O ( ∣ T ∣ ) O(|T|) O(∣T∣)
F. A Horrible Poem
问题描述
给出一个由小写英文字母组成的字符串 S S S,再给出 q q q 个询问,要求回答 S S S 某个子串 S l . . . r S_{l...r} Sl...r 的最短循环节的长度
解题思路
设 S l . . . r S_{l...r} Sl...r 的最短循环节长度为 x x x,则其一定满足 f ( S l . . . r − x ) = f ( S l + x . . . r ) f(S_{l...r-x})=f(S_{l+x...r}) f(Sl...r−x)=f(Sl+x...r)
那么显然有 x x x 为子串长度的某个因子( x × k = r − l + 1 x\times k =r - l + 1 x×k=r−l+1)
所以做法一为 n \sqrt{n} n 枚举 r − l + 1 r - l + 1 r−l+1 的所有约数然后找到最小的 x x x,时间复杂度 q ∣ S ∣ q\sqrt{|S|} q∣S∣
但是此题卡此根号做法
由于 x × k = r − l + 1 x\times k =r - l + 1 x×k=r−l+1
考虑将 k k k 进行质因子分解,即:
k = p 1 a 1 p 2 a 2 . . . p m a m k=p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{m}^{a_{m}} k=p1a1p2a2...pmam,那么 p 1 p 2 . . . p m p_{1}p_{2}...p_{m} p1p2...pm 同样为 r − l + 1 r-l+1 r−l+1 的质因子,并且 ( r − l + 1 ) / ( p 1 . . . p 2 ) (r - l + 1)\ / \ (p_{1}...p_{2}) (r−l+1) / (p1...p2) 一定也为循环节,故枚举 r − l + 1 r - l + 1 r−l+1 的所有约数,找到最小的 x x x 即可
总时间复杂度 O ( q l o g 2 ∣ S ∣ ) O(qlog_{2}^{|S|}) O(qlog2∣S∣)
H. Antisymmetry
题目描述
对于一个 0 / 1 字符串,如果将这个字符串 0 和 1 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如 00001111 和 010101 就是反对称的,而 1001 就不是
现在给出一个长度为
n
n
n 的 0 / 1 字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算
解题思路
如果一个串满足上述条件,那么以其中心向两边扩展的所有子串均满足要求,故可用马拉车算法求解,若用 H a s h Hash Hash 算法,则枚举中心轴,二分找到满足条件的最长子串即可,总时间复杂度 O ( n l o g 2 n ) O(nlog_{2}^{n}) O(nlog2n),若用马拉车算法可将其优化至 O ( n ) O(n) O(n)