使用python3进行实现
关于KMP算法,其时间复杂为F(n+m),即只需遍历一次字符串就能找出第一个匹配的字符,实现的核心就是求解next数组,
而next数组,实际上就是一个存放着当匹配到这一位失去匹配时,模式串的平移位数。
例如模式串abcabca
,当在最后一位匹配失败的时候,如果是使用一般做法的话,就是将遍历两个字符串的下标重置,当是实际上只需要将模式串平移三位,用第四位的a继续进行匹配就行了。
关于next数组的求解理解起来感觉有点困难,首先要理解最长前缀的概念,这也是求解这个数组的最主要原理
下面是最长前缀的几个例子
abca的最长前缀是a,ab没有最长前缀,abcabc的最长前缀是abc
之后就是理解最长前缀的用处了,以abcabcabca
为例子,如果在前四位匹配出错,那么都是直接往下继续匹配就行了,模式串不需要平移,只有当第五位的b出现失配,模式串才需要平移一位(这里其实还有优化方案,因为b和第二位的b一样,实际上不需要匹配,这个可以优化,这里就暂时不做讨论)
平移一位之后就是用第二位的b进行匹配,同理可知第六位失去匹配的情况,字符串要平移两位,而这个平移位数就是由最长前缀决定的,在第五位b前面的字符,最长前缀为a,长度为一
因此平移一位
那么求解数组就简化成一个问题,那就是求解一个字符串每一位前面的字符串的最长前缀,而第一位则为-1,如果为-1则表示模式串的第一位与被匹配字符串当前位置的字符不匹配
next数组求解的代码:
def next(target):
#模式串最长前缀串的下标
j = -1
#next数组,规定第一位为-1,当匹配时为-1时则表示第一位与当前被匹配字符串的当前字符不匹配
next = [-1]
#初始化next数组
i = 1 #从第二位开始
while i < len(target):
next.append(0)
i+=1
i = 0
while i < len(target) - 1:#next数组求解是按下一位赋值的,因此为要减一
if j==-1 or target[i] == target[j]:
i+=1
j+=1
next[i] = j
else:
j = next[j]#当失去匹配时回溯到前一位的最长前缀的下标
return next
求解的代码的实现十分的简单,这个算法实现了只遍历一遍字符串就求得next数组,实际上如果你不理解也可以采用两个for循环进行求解。。。
主要难以理解的地方还是j = next[j]
这一句,这一句主要是为了得到之前的最长前缀,这样如果这个位置的字符匹配的话就可以直接加一
回到之前那个优化的问题,abcabcabca
,当第五位的b出现失配,模式串平移一位,当实际上并不需要比较b,而是应当重置为第一位,因为就算是从第二位比较,也会因为不匹配而退回第一位,这样子的话就会导致多匹配一次,这个时候可以这样修改一下匹配的代码
if j==-1 or target[i] == target[j]:
i+=1
j+=1
if target[i] == target[j] :
next[i] = next[j]
else:
next[i] = j
else:
j = next[j]
如果匹配的话就回溯到匹配成功的字符下标对应的最长前缀
进行匹配的代码:
#返回的是第一次匹配的下标,0位第一位
def strStr(source, target):
#排除字符空的情况
if target is None or source is None:
return -1
#得到next数组
list = next(target)
i = 0
j = 0
l1 = len(source)
l2 = len(target)
while i < l1 and j < l2:
#当为-1的时候说明当前位与第一位并不匹配,可以直接跳过
if j==-1 or source[i] == target[j]:
i+=1
j+=1
else:
j = list[j]
if j == l2:
return i-j
else:
return -1