python中KMP算法实例

def get_prefix(nums):
    """设置prefix前缀表左指针为0"""
    prefix_next = 0
    """设置nums模式串左指针为0"""
    nums_i = 1
    nums = list(nums)
    """默认前缀表第一位为0"""
    prefix = [0]
    """当模式串左指针小于模式串长度时循环"""
    while nums_i < len(nums):
        """
            当模式串中前缀表左指针对应数值和模式串左指针对应数值一致时,前缀表左指针+1,并且添加至前缀表,模式串左指针+1
        """
        if nums[prefix_next] == nums[nums_i]:
            prefix_next += 1
            nums_i += 1
            prefix.append(prefix_next)
        else:
            """
                不相同,并且前缀表左指针大于0时,
                前缀表左指针等于  前缀表中,当前前缀表左指针数值-1 所对应的值,
                前缀表左指针小于等于0时,前缀表左指针肯定为0,前缀表中加入,并且模式串左指针+1
            """
            if prefix_next > 0:
                prefix_next = prefix[prefix_next - 1]

为什么:
prefix_next = prefix[prefix_next - 1]

例:
下标     0   1   2   3   4   5   6   7   8   9   1文本串 A   B   A   B  C   A   B   A   B  A   A前缀表 0   0   1   2   0   1   2   3   4   3   1
当前假设我们要求的是:
在这里插入图片描述
拓展:前缀为不包括最后一位的字符串,后缀为前缀表中不含第一位的字符串,例:
公共的意思就是一样,细品看图琢磨,肯定可以懂
在这里插入图片描述

当前prefix_next 为3,最长公共前后缀为ABA:
在这里插入图片描述

当前prefix_next在这:
![在这里插入图片描述](https://img-blog.csdnimg.cn/d6780e62e8b04d76a7460f50387f6882.png
我们需要知道 前缀 中的最长公共缀是多少,比如例中前缀(注意是前缀不是前缀表)前缀的最长公共缀为1,也就是ABA中的 A 和A。
前缀表001也就代表ABA最长公共缀为1,那么此时ABA的下标012

在这里插入图片描述
因为这两个框里面是一样的 所以前缀的最长公共缀就是后缀的最长公共缀 所以才有上面那个截图的说法
在这里插入图片描述

我们就只要判断下标为1的值与下标为10的值就可以了。ps 最后一根下标是10,打错了
如何得到下标1的值也就是prefix_next = prefix[prefix_next - 1]:
此时prefix_next 就为1 prefix_next= prefix[3 - 1]:

关键点 当前前缀表除开最后一位的前缀的最长公共缀,就是后缀的最长公共缀,所以才只需要比较文本串中的下标1和下标10

在这里插入图片描述

PS 如果有疑问可以用模式串ABABCABABA , 前缀表0012012343 再去推一边,结果是一样的 ,
KMP算法很精妙有趣,可能我这边说的有点含糊,不过按照这个思路去推,理清后就很清晰,而且基本上不会忘记了

            else:
                prefix.append(prefix_next)
                nums_i += 1
    """
        将前缀表整体向后移一位,最后一位消失,第一位默认为-1,前缀表整体长度不变
    """
    new_prefix = [-1]
    prefix.pop(len(prefix) - 1)
    for i in prefix:
        new_prefix.append(i)
    """返回前缀表"""
    return new_prefix


def query(text, nums):
    count = 0
    nums = list(nums)
    text = list(text)
    prefix = get_prefix(nums)
    text_next, nums_next = 0, 0
    """
        nums_next 为模式串左指针,text_next为文本串左指针, nums为模式串, text为文本串,prefix为前缀表,
        通过前缀表判断文本串中是否存在模式串
    """
    while text_next < len(text):
        """
            如果模式串左指针大小等于文本串大小,并且当前模式串最后一位与文本串最后一位相等,则找到了对应的,
            计数器+1,
            模式串左指针回溯至前缀表中索引对应的值
        """
        if nums_next == len(nums)-1 and nums[nums_next] == text[text_next]:
            count += 1
            nums_next = prefix[nums_next]
        """
            如果文本串第一位与模式串第一位相对,则都+1
        """
        if text[text_next] == nums[nums_next]:
            nums_next += 1
            text_next += 1
        else:
            """
                不相对的情况下,模式串左指针等于前缀表中索引对应的值,
                如果模式串左指针等于-1了,则全部+1
            """
            nums_next = prefix[nums_next]
            if nums_next == -1:
                nums_next += 1
                text_next += 1
    return count


if __name__ == '__main__':
    print(query("POKUYAAAABABAAAACACDAA", "AAAA"))

在这里插入图片描述
根据模式串生成前缀表关键(len = prefix[len - 1]),并重构前缀表第一位变成-1,其余先前移动一位 保证前缀表总长度不变,
比较模式串和文本串,如果遇到不匹配的,根据当前模式串指针位置,找到前缀表该指针位置索引对应的值(j = prefix[j]),这个值就是新的指针索引,继续比较
ps. 如有疑问请评论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值