蓝桥杯前夕。虽然被这比赛坑过,但该打的成就还是要打的。所以今晚就复习一下很久没有碰的KMP吧。
KMP是个很优秀的字符串匹配算法(废话),其时间复杂度为
O
(
n
+
m
)
O(n+m)
O(n+m)。
使用KMP进行匹配之前,需要预处理出来一个数组,名为next
,顾名思义为失配后指针要移动到的下一个位置。
next数组的值还可以理解为(以当前字符的前一位为末尾)并且(和当前字符串的前缀相同)的最大字符串的长度。
举个例子:
a b c d a b c d a b c e
next:-1 0 0 0 0 1 2 3 4 5 6 7 0
计算时采用双指针的形式,i
在后面,j
在前面。
首先规定next[0]=-1
,因为next
数组表示的是前一位结尾的字符串和模式串的最大公共前缀,所以0前面是-1,无意义。next[1]=0
,因为它本身等于它本身无意义,不能给后面的计算带来帮助,所以置为0。
假设j
前面的字符都已经和i
前面的字符匹配了,那么当p[i]=p[j]
时,next[i+1]=next[i]+1
,也可以用j+1
表示(上文高亮部分)。
当p[i]!=p[j]
时:
此时,以第i
个字符为结尾的后缀(即p[i-next[i],i]
)和字符串p[0,j]
不相同,因为最后一位没有匹配。按照暴力做法,此时应该让j
归为0,i
往前移j-1
位准备重新比较。
但是观察next[j]
可以发现,我们可以利用next[j]
直接得知第i
个位置前面的长度为next[j]
的字符串(即"abc"
)和第j
个位置前面的长度为next[j]
的字符串是一样的。即(p[i-next[j],i-1]=p[j-next[j],j-1]
),而p[j-next[j],j-1]
又和p[0,next[j]-1]
相同,所以此时可以直接认为最前面的next[j]
位已经比较过了,现在轮到p[next[j]]
和p[i]
比较了。在程序中的操作为j=next[j]
,i
不变。
由此递推得到next
数组。
进行匹配时同理,也是进行这种j=next[j]
的操作来降低时间复杂度。