使用场景
哈希匹配算法比较适合适用于多次对固定串内部匹配的算法。
预备理解
哈希匹配算法的原理是采用一种哈希函数将字符串映射成一个数字,之后就演化为两种方法:
第一种:假如数字相同,则去遍历匹配两串;
第二种:采用两种哈希函数,假如两组不同哈希映射后得到的数字相同则判断为字符能够匹配的上。
第二种方法的速度快但是存在误判即将不能够匹配的上判断为匹配的上,只是若是哈希函数设计的好这种概率会非常的低。
这里采用的哈希函数为:
K
=
(
C
0
∗
B
a
s
e
0
+
C
1
∗
B
a
s
e
1
+
C
3
∗
B
a
s
e
3
+
.
.
.
)
(
m
o
d
P
)
K=(C_0*Base^0+C_1*Base^1+C_3*Base^3+...) (mod P)
K=(C0∗Base0+C1∗Base1+C3∗Base3+...)(modP)
其中
B
a
s
e
Base
Base和
P
P
P一般选择为素数
但是假如是求子串中
(
l
,
r
)
(l, r)
(l,r)中位置的部分哈希映射后的值,则变为
K
=
(
C
l
∗
B
a
s
e
0
l
+
C
l
+
1
∗
B
a
s
e
1
+
C
l
+
2
∗
B
a
s
e
3
+
.
.
.
)
(
m
o
d
P
)
K=(C_l*Base^0l+C_{l+1}*Base^1+C_{l+2}*Base^3+...) (mod P)
K=(Cl∗Base0l+Cl+1∗Base1+Cl+2∗Base3+...)(modP)
但是很明显在预处理时只能处理出从头开始的字符,那么问题就变成了
K
=
(
C
l
∗
B
a
s
e
l
l
+
C
l
+
1
∗
B
a
s
e
l
+
1
+
C
l
+
2
∗
B
a
s
e
l
+
3
+
.
.
.
)
/
B
a
s
e
l
(
m
o
d
P
)
K=(C_l*Base^{l}l+C_{l+1}*Base^{l+1}+C_{l+2}*Base^{l+3}+...)/Base^l (mod P)
K=(Cl∗Basell+Cl+1∗Basel+1+Cl+2∗Basel+3+...)/Basel(modP)
但是在外面整体求模后是不能直接除以
B
a
s
e
l
Base^l
Basel,所以这里面涉及了求模的逆元的运算。
逆元
求模的逆元,代表这
x
∗
x
−
1
=
1
(
m
o
d
P
)
x * x^{-1}=1(modP)
x∗x−1=1(modP),例如在mod7的情况下2就是4的逆元,因为(2 * 4)% 7 = 1,在里面就可以将除法算式转化为乘法算式,而如何求一个数在mod下的逆元呢,推理过程为:
x
×
x
−
1
≡
1
(
m
o
d
P
)
令
:
P
%
x
=
r
P
=
k
x
+
r
k
x
+
r
≡
0
(
m
o
d
P
)
k
r
−
1
+
x
−
1
≡
0
(
m
o
d
P
)
x
−
1
≡
−
k
r
−
1
(
m
o
d
P
)
\begin{aligned} x\times x^{-1}&\equiv1\ (mod\ P) \\ 令:P\%x&=r \\ P &= kx+r \\ kx+r &\equiv0\ (mod\ P) \\ kr^{-1}+x^{-1} &\equiv0\ (mod\ P) \\ x^{-1} &\equiv-kr^{-1}\ (mod\ P) \end{aligned}
x×x−1令:P%xPkx+rkr−1+x−1x−1≡1 (mod P)=r=kx+r≡0 (mod P)≡0 (mod P)≡−kr−1 (mod P)
很显然其中
k
=
P
/
x
,
r
=
P
k=P/x,r=P
k=P/x,r=P%
x
x
x
代码
下面就是通过哈希匹配算法进行字符串匹配的代码实现:
init()函数用来初始化逆元数组,Base数组以及前缀和数组。
通过两个哈希映射得到区间字符串的哈希值,比较两个哈希值即可判断字符是否相等。