KMP原理及相关代码(python3实现)

    关于KMP算法,相信大家都有所了解。它也是我们在数据结构书上遇到的第一个
    比较繁琐的问题。希望我能通过这篇文章对大家有所启发。
    KMP算法主要找到子串在模式串中第一次出现的位置,也是我们在实际情况中
    经常会遇见的一个问题。闲话少谈,我们来看以下例子。
    
    现假设有以下两个字符串s1, s2。
s1ababcacc
s2abca
    对于暴力求解方式来说,我们只需要对s1和s2这两个字符串逐个进行比较。
    如果两个字符相同,则比较下一个字符。如果不相同,
    则将s2子串向后移位,再重新进行比较。直到将s2遍历完或s2的尾端对齐s1的尾端,则算法结束。
    其大致的执行过程如下图所示。
在这里插入图片描述
	我们可以很清楚的看到再进行第二次判断时,指针会有回溯
	(即s1指针从指向a反向跳转为指向b)。那么我们能不能找到一个方法,让指向s1值的指针不回溯呢?
	KMP算法就因此而产生。
	要想了解kmp算法,我们得先知道相同最长前缀和最长后缀的概念。
	对应下表s1字符串中的来说,其相同的最长前缀和后缀的值如下表所示。
s1ababc
-10012
	我们以s1串中的c字符为例:
	c字符前缀:										c字符后缀:
	a													b
	ab													ab
	aba													bab
	abab												abab
	去除一定相同的abab,它相同的最大的前缀串和后缀串为ab,长度为2。
	各个值的相同最长前缀和后缀构成了一个next数组。

	KMP基本的思想是利用next数组,使得字符串直接从不匹配的地方开始接着进行匹配。
	如图示例:
在这里插入图片描述
	我们已经知道了next数组的基本概念和如何计算,那计算机该如何实现呢?
	我们可以用动态规划的方式加以考虑。
	
	对于一个字符串p,假设我们已经知道:

| 在这里插入图片描述

	如果此时Pk = Pj  则next[j+1] = next[j]+1     又得知next[j] = k-1,故next[j+1] = k
	如果此时Pk != Pj,又会怎样呢?
	我们可令k = next[k] ;如果p[k] = p[j] 则next[j+1] = k+1否则重复此过程。此处可能不太好理解。
	我们直接来看图

| 在这里插入图片描述
关于不相等的时候递归确实比较难以理解,大家可以多画画图。加深自己的理解。

def gen_next(s2):
    k = -1
    n = len(s2)
    j = 0
    next_list = [0 for i in range(n)]
    next_list[0] = -1                           #next数组初始值为-1
    while j < n-1:
        if k == -1 or s2[k] == s2[j]:
            k += 1
            j += 1
            next_list[j] = k                    #如果相等 则next[j+1] = k
        else:
            k = next_list[k]                    #如果不等,则将next[k]的值给k
    return next_list


def match(s1, s2, next_list):
    ans = -1
    i = 0
    j = 0
    while i < len(s1):
        if s1[i] == s2[j] or j == -1:
            i += 1
            j += 1
        else:
            j = next_list[j]
        if j == len(s2):
            ans = i - len(s2)
            break
    return ans


if __name__ == '__main__':
    s1 = 'ababaacc'
    s2 = 'abaa'
    next_list = gen_next(s2)
    print(next_list)
    print(match(s1, s2, next_list))


	关于kmp还有一个改进的next数组,即倘若p[k] = p[j]的时候直接将next[k]的值赋给next[j+1],
	大家不妨思考一下这么做会有什么好处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值