基本思想
目标(target):主串
模式(pattern):要在主串中寻找的子串
从左往右将模式的每个字符和对应位置的目标字符比较,在模式的第j位不匹配称为失配,则模式中失配位置之前的子串
P
0
P
1
.
.
.
P
j
−
1
P_0P_1...P_{j-1}
P0P1...Pj−1全部匹配上:
T
:
T
0
T
1
.
.
.
T:T_0T_1...
T:T0T1...
T
s
T
s
+
1
.
.
.
T
s
+
j
−
1
T_sT_{s+1}...T_{s+j-1}
TsTs+1...Ts+j−1
T
s
+
j
T_{s+j}
Ts+j
.
.
.
T
n
−
1
...T_{n-1}
...Tn−1
P
P
P:
P
0
P
1
P_0P_1
P0P1
.
.
.
P
j
−
1
...P_{j-1}
...Pj−1
P
j
P_j
Pj
.
.
.
P
m
...P_m
...Pm
若此时在模式P中存在最大的
k
k
k,使得
P
0
.
.
.
P
k
=
P
j
−
k
−
1
.
.
.
P
j
−
1
P_0...P_k=P_{j-k-1}...P_{j-1}
P0...Pk=Pj−k−1...Pj−1,即模式
P
P
P中有首尾重叠的部分,则下次比较可以将模式P向后移动
j
−
k
−
1
j-k-1
j−k−1位:
T
:
T
0
T
1
.
.
.
T:T_0T_1...
T:T0T1...
T
s
T
s
+
1
.
.
.
T_sT_{s+1}...
TsTs+1...
T
s
+
j
−
k
−
1
.
.
.
T
s
+
j
−
1
T_{s+j-k-1}...T_{s+j-1}
Ts+j−k−1...Ts+j−1
T
s
+
j
T_{s+j}
Ts+j
.
.
.
T
n
−
1
...T_{n-1}
...Tn−1
P
P
P:
P
0
P
1
P_0P_1
P0P1
.
.
.
...
...
P
j
−
k
−
1
P_{j-k-1}
Pj−k−1
.
.
.
P
j
−
1
...P_{j-1}
...Pj−1
P
j
P_j
Pj
.
.
.
P
m
...P_m
...Pm
P
P
P:
P
0
P_0
P0
.
.
.
P
k
...P_{k}
...Pk
P
k
+
1
.
.
.
P
j
.
.
.
P
m
P_{k+1}...P_j...P_m
Pk+1...Pj...Pm
由
k
k
k的极大性可以保证将模式P向后移动少于
j
−
k
−
1
j-k-1
j−k−1位不会匹配,因为若匹配上了,意味着有更大的
k
k
k满足
P
0
.
.
.
P
k
=
P
j
−
k
−
1
.
.
.
P
j
−
1
P_0...P_k=P_{j-k-1}...P_{j-1}
P0...Pk=Pj−k−1...Pj−1,从而破坏了
k
k
k的极大性。
由上述分析可知对于某个
j
j
j,
k
k
k的取值只和模式
P
P
P有关,从而对于固定的
P
P
P,
k
k
k是
j
j
j的函数,记
k
=
n
e
x
t
[
j
]
k=next[j]
k=next[j],
n
e
x
t
[
j
]
next[j]
next[j]表达式,后续给出其求法:
n
e
x
t
[
j
]
=
{
−
1
j
=
0
k
+
1
k
为
满
足
0
⩽
k
<
j
−
1
且
P
0
.
.
.
P
k
=
P
j
−
k
−
1
.
.
.
P
j
−
1
的
最
大
整
数
0
else
next[j]=\begin{cases} -1 &\text{} j=0 \\ k+1 &\text{} k为满足0\leqslant k<j-1且P_0...P_k=P_{j-k-1}...P_{j-1}的最大整数 \\ 0 &\text{else } \end{cases}
next[j]=⎩⎪⎨⎪⎧−1k+10j=0k为满足0⩽k<j−1且P0...Pk=Pj−k−1...Pj−1的最大整数else
算法流程
s
t
a
r
t
start
start:从目标
T
T
T的
s
t
a
r
t
start
start(从0开始)处开始匹配模式
P
P
P
l
e
n
_
m
a
t
c
h
len\_match
len_match:
P
P
P中已经匹配上的长度,也即
P
P
P进行匹配的起始位置,初始为0
l
e
n
g
t
h
_
t
a
r
g
e
t
length\_target
length_target:
T
T
T的长度
l
e
n
g
t
h
_
p
a
t
t
e
r
n
length\_pattern
length_pattern:
P
P
P的长度
n
e
x
t
next
next:数组,表示
P
P
P的
n
e
x
t
next
next向量
1、若
l
e
n
g
t
h
_
p
a
t
t
e
r
n
+
s
t
a
r
t
>
l
e
n
g
t
h
_
t
a
r
g
e
t
length\_pattern + start> length\_target
length_pattern+start>length_target则返回-1(表示匹配失败)
2、从左到右逐一比较模式
P
l
e
n
_
m
a
t
c
h
.
.
.
P
m
P_{len\_match}...P_m
Plen_match...Pm和目标的字符,若全部匹配则返回目标匹配的起始位置
s
t
a
r
t
start
start;否则假设直到
P
j
P_j
Pj不匹配:
若
n
e
x
t
[
j
]
=
−
1
next[j]=-1
next[j]=−1(即
j
=
0
j=0
j=0,第一个字符就匹配失败),则
s
t
a
r
t
start
start自增,转1;
否则将
s
t
a
r
t
start
start增至
P
P
P与原来的
P
P
P有
n
e
x
t
[
j
]
next[j]
next[j]个重合,根据
n
e
x
t
[
j
]
next[j]
next[j]定义,重合部分
T
T
T和
P
P
P全部匹配,从而只需要从下一个位置,即
n
e
x
t
[
j
]
next[j]
next[j]开始继续比较,,故令
l
e
n
_
m
a
t
c
h
=
n
e
x
t
[
j
]
len\_match=next[j]
len_match=next[j],转1。
例如
T
=
a
c
a
b
a
a
b
a
a
b
c
a
c
a
a
b
c
,
P
=
a
b
a
a
b
c
a
c
时
的
匹
配
过
程
如
下
:
T=acabaabaabcacaabc,P=abaabcac时的匹配过程如下:
T=acabaabaabcacaabc,P=abaabcac时的匹配过程如下:
next向量计算
由定义
n
e
x
t
[
0
]
=
−
1
next[0]=-1
next[0]=−1,表示
P
0
P_0
P0失配时
s
t
a
r
t
start
start需要向后移动1位重新开始匹配。
n
e
x
t
[
1
]
=
0
next[1]=0
next[1]=0(定义中的else情况)。
对于失配位置
j
j
j,
n
e
x
t
[
j
]
next[j]
next[j]的意义为
P
0
P
1...
P
j
−
1
P_0P1...P_{j-1}
P0P1...Pj−1首尾能重叠的最大长度,即满足
P
0
.
.
.
P
k
=
P
j
−
k
−
1
.
.
.
P
j
−
1
P_0...P_k=P_{j-k-1}...P_{j-1}
P0...Pk=Pj−k−1...Pj−1的最大的
k
+
1
,
0
⩽
k
<
j
−
1
k+1,0\leqslant k<j-1
k+1,0⩽k<j−1,若不存在重叠部分即为0。
为了求
n
e
x
t
[
j
]
next[j]
next[j],可以考虑其与
n
e
x
t
[
j
−
1
]
next[j-1]
next[j−1]的关系。
1、设
k
=
n
e
x
t
[
j
−
1
]
,
j
>
1
k=next[j-1],j>1
k=next[j−1],j>1,则由
n
e
x
t
[
j
−
1
]
next[j-1]
next[j−1]的意义有
P
0
.
.
.
P
k
−
1
=
P
j
−
k
−
1
.
.
.
P
j
−
2
P_0...P_{k-1}=P_{j-k-1}...P_{j-2}
P0...Pk−1=Pj−k−1...Pj−2:
P
0
P
1
P_0P_1
P0P1
.
.
.
...
...
P
j
−
k
−
1
P_{j-k-1}
Pj−k−1
.
.
.
P
j
−
2
...P_{j-2}
...Pj−2
P
j
−
1
P
j
.
.
.
P
m
P_{j-1}P_j...P_m
Pj−1Pj...Pm
P
0
P_0
P0
.
.
.
P
k
−
1
...P_{k-1}
...Pk−1
P
k
P_k
Pk
.
.
.
P
j
.
.
.
P
m
...P_j...P_m
...Pj...Pm
2、若
P
k
=
P
j
−
1
P_k=P_{j-1}
Pk=Pj−1,则有
P
0
.
.
.
P
k
=
P
j
−
k
−
1
.
.
.
P
j
−
1
P_0...P_k=P_{j-k-1}...P_{j-1}
P0...Pk=Pj−k−1...Pj−1,且此
k
k
k具有极大性,从而
n
e
x
t
[
j
]
=
n
e
x
t
[
j
−
1
]
next[j]=next[j-1]
next[j]=next[j−1],返回。
3、若
P
k
≠
P
j
−
1
P_k\neq P_{j-1}
Pk=Pj−1,则需要寻找最大的
k
′
k'
k′使得
P
0
.
.
.
P
k
′
=
P
j
−
k
′
−
1
.
.
.
P
j
−
1
P_0...P_{k'}=P_{j-k'-1}...P_{j-1}
P0...Pk′=Pj−k′−1...Pj−1成立:
P
0
P
1
P_0P_1
P0P1
.
.
.
...
...
P
j
−
k
−
1
.
.
.
P
j
−
k
′
−
1
.
.
.
P
j
−
2
P_{j-k-1}...P_{j-k'-1}...P_{j-2}
Pj−k−1...Pj−k′−1...Pj−2
P
j
−
1
P_{j-1}
Pj−1
P
j
.
.
.
P
m
P_j...P_m
Pj...Pm
P
0
P_0
P0
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
P
k
−
1
.................P_{k-1}
.................Pk−1
P
k
P_k
Pk
.
.
.
P
j
.
.
.
P
m
...P_j...P_m
...Pj...Pm
P
0
P_0
P0
.
.
.
P
k
′
−
1
...P_{k'-1}
...Pk′−1
P
k
′
P_{k'}
Pk′
.
.
.
P
m
...P_m
...Pm
于是有
P
0
.
.
.
P
k
′
−
1
=
P
k
−
k
′
.
.
.
P
k
−
1
P_0...P_{k'-1}=P_{k-k'}...P_{k-1}
P0...Pk′−1=Pk−k′...Pk−1,即
k
′
=
n
e
x
t
[
k
]
k'=next[k]
k′=next[k]。
4、若
k
′
=
−
1
,
则
n
e
x
t
[
j
]
=
0
,
否
则
令
k
=
k
′
k'=-1,则next[j]=0,否则令k=k'
k′=−1,则next[j]=0,否则令k=k′,转2.
例如
P
=
a
b
a
a
b
c
a
c
的
n
e
x
t
向
量
计
算
过
程
如
下
:
P=abaabcac的next向量计算过程如下:
P=abaabcac的next向量计算过程如下:
代码
代码在这里
图片来源
例子的图片来自清华大学殷人昆和王宏的数据结构电子教案。