KMP算法是一种高效的字符串匹配算法,比较难以理解,下面只是我的一点个人理解,以后有更深入的理解会陆续加进去的。
1. 比较简单的一种字符串匹配算法
String S; String T;
S是待匹配串,T是匹配串,又称模式串。所以字符串匹配又称模式匹配。
S: |
||||||||||||||
A |
B |
A |
B |
C |
A |
B |
C |
A |
C |
B |
A |
B |
C |
B |
A |
B |
C |
A |
C |
|
|||||||||
T: |
简单的方法,一个循环即可,但是时间复杂度确实o(n*n)。
看着是一个循环,但是由于可能存在的不断的回溯,导致时间复杂度的提高。
2. KMP的出现就是为了解决这种回溯问题,使得时间复杂度成为线性复杂度,o(n+m).
KMP的思想就是不要回溯i指针,必要的时候移动模式串j指针的位置。
一:
S: |
||||||||||||||
A |
B |
A |
B |
C |
A |
B |
C |
A |
C |
B |
A |
B |
C |
B |
A |
B |
C |
A |
C |
|
|
|
|
||||||
T: |
二:
注意,C与A不匹配的时候,如果是一般匹配算法,那么i要回溯到第一个B的位置,即:
S: |
||||||||||||||
A |
B |
A |
B |
C |
A |
B |
C |
A |
C |
B |
A |
B |
C |
B |
|
A |
B |
C |
A |
C |
|
|
|
|
|||||
T: |
就像是这样的。
但是注意到,B与A肯定是不匹配的,所以拉回两个更加合适,KMP算法大概就是这个意思,省略去已经比较过的不用再比较子串。
那么KMP算法最重要的问题就是如何寻找当串不匹配时,j应该拉回到那个位置比较合适。在此有个公式:
由此定义可以推断出下面T字符串的next[j]的值:
J |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
模式串 |
A |
B |
A |
A |
B |
C |
A |
C |
Next[j] |
0 |
1 |
1 |
2 |
2 |
3 |
1 |
2 |
有了这个理念,再求匹配就方便了,程序如下:
下面问题是:如何求next[j] ?
你可能会说,不是有公式了吗?直接用公式求不就行了吗?
但是公式中的求法无法直接搬到程序中,很不方便,下面的方法使得寻找next[j]时间复杂度为o(m)
由定义可知:
Next[1]=0;
设:next[j]=k,那么存在下列等式:
此时next[j+1]=?
1) 若,说明模式串中存在等式 :
并且不可能再存在比这个K更大的K存在,符合next[j]公式的定义。那么next[j+1]=next[j]+1;
2) 如果 ,那么剩下的问题是一个新的模式匹配问题,整个模式串既是主串又是模式串,在匹配的过程中,由于
是成立的,则此时应将模式向右滑动至第next[k]个字符串和主串中的第j个字符相比较。这个过程是迭代过程,即有:next[j+1]=next[k]+1。
如果一直不满足匹配状态,那么next[j+1]=1。
综上所述,可得算法: