KMP算法——全网最通俗易懂!!!!!

写一下个人对KMP算法的看法

1、我们在做字符串匹配问题时,用暴力方法来进行遍历是O(m*n)的,也就是每次不匹配时,模式串都回到开头,并往右移一位,模式串再次从开头进行匹配,而KMP算法解决的问题就是——不匹配时,是否能将模式串往后多移几位,并且模式串无需再从开头进行匹配,这样达到O(m+n)复杂度,也就是两个字符串都只遍历1次(模式串为常数次)

2、如何做到?假设模式串和符号串当前对比的字符不匹配,那它俩之前的字符肯定是匹配成功了的。那我考查模式串这之前字符的最长相等前后缀(这个自行搜索一下,概念很简单——例如abcab,前两个字符ab和后两个字符ab相等且长度最长了,那么这个字符串abcab的最长相等前后缀长度=2),那我此时去移动模式串,是不是就可以把模式串的前缀,和原文串里面的模式串后缀给匹配上,那这部分就不用我再次进行匹配了,我直接考察匹配完了的下一个字符,于是我们定义了next数组

3、那这个next数组是什么呢,就是计算模式串每一位截止的字串最长前后缀长度,放在对应的位置(由概念可得next[0] = 0)

4、每次模式串p[j]和原文串s[i]不匹配了,模式串就回退到next[j-1]的位置(可以看作将模式串大幅右移且前后缀匹配,直接考察了下一个字符)

5、求next数组的思想:累计+KMP思想
这是我觉得KMP算法一个很奇妙的点,它在进行KMP算法前置工作时,已经用到了 KMP的思想,具体我写在代码注释里面了

其中j指向模式串最后一位
i指向原文串最后一位

版本1

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        def get_next(s):
            next = [0] * len(s)
            next[0] = 0 # 第一个为0
            #j指向模式串的最后一个字母s[j]
            #i指向next数组填充位置,也就是原文串的末尾字母为s[i]
            #我们不停地将模式串的最后一个字母,也就是s[j]去和s[i]做匹配
            j = 0
            for i in range(1, len(s)):
                #成功匹配,做累计,j+=1 即代表模式串增长了
                if s[j] == s[i]:
                    next[i] = j + 1
                    j += 1
                #匹配失败,即模式串最后一位 != 原文串最后一位,
                #根据kmp思想,借助前后缀相等来回退
                else:
                    #回退模式串
                    #此时next在i之前的部分已经填充了
                    while j > 0 and s[j] != s[i]:
                        j = next[j - 1]
                    #已经退到模式串首位了,并且还是不匹配,这时候代表没有相等前后缀
                    if j == 0 and s[j] != s[i]:
                        next[i] = 0
                    #否则,都算是匹配上了,和最上面if情况一样做累计
                    else:
                        next[i] = j + 1
                        j += 1
            return next
        next = get_next(needle)
        i = 0
        j = 0
        while i < len(haystack):
            if haystack[i] == needle[j]:
                j += 1
                i += 1
            #不匹配,开始回退needle
            else:
                while j > 0 and haystack[i] != needle[j]:
                    j = next[j-1]
                #如果匹配了,一起往后走
                if haystack[i] == needle[j]:
                    i += 1
                    j += 1
                #否则只有haystack往后走
                else:
                    i += 1
            #needle匹配完了
            if j == len(needle):
                return i - len(needle)
        return -1

版本2

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        def get_next(s):
            next = [0] * len(s)
            next[0] = 0 # 第一个为0
            #j指向模式串的最后一个字母s[j]
            #i指向next数组填充位置,也就是原文串的末尾字母为s[i]
            #我们不停地将模式串的最后一个字母,也就是s[j]去和s[i]做匹配
            j = 0
            for i in range(1, len(s)):
                #成功匹配,做累计,j+=1 即代表模式串增长了
                if s[j] == s[i]:
                    next[i] = j + 1
                    j += 1
                #匹配失败,即模式串最后一位 != 原文串最后一位,
                #根据kmp思想,借助前后缀相等来回退
                else:
                    #回退模式串
                    #此时next在i之前的部分已经填充了
                    while j > 0 and s[j] != s[i]:
                        j = next[j - 1]
                    #已经退到模式串首位了,并且还是不匹配,这时候代表没有相等前后缀
                    if j == 0 and s[j] != s[i]:
                        next[i] = 0
                    #否则,都算是匹配上了,和最上面if情况一样做累计
                    else:
                        next[i] = j + 1
                        j += 1
            return next
        next = get_next(needle)
        i = 0
        j = 0
        while i < len(haystack):
            if haystack[i] == needle[j]:
                j += 1
                i += 1
            #不匹配,开始回退needle
            else:
                while j > 0 and haystack[i] != needle[j]:
                    j = next[j-1]
                #回退到0了,还是不匹配,那只能haystack往后走了
                if haystack[i] != needle[j]:
                    i += 1
            #needle匹配完了
            if j == len(needle):
                return i - len(needle)
        return -1

版本3

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        def get_next(s):
            next = [0] * len(s)
            next[0] = 0 # 第一个为0
            #j指向模式串的最后一个字母s[j]
            #i指向next数组填充位置,也就是原文串的末尾字母为s[i]
            #我们不停地将模式串的最后一个字母,也就是s[j]去和s[i]做匹配
            j = 0
            for i in range(1, len(s)):
                #成功匹配,做累计,j+=1 即代表模式串增长了
                if s[j] == s[i]:
                    next[i] = j + 1
                    j += 1
                #匹配失败,即模式串最后一位 != 原文串最后一位,
                #根据kmp思想,借助前后缀相等来回退
                else:
                    #回退模式串
                    #此时next在i之前的部分已经填充了
                    while j > 0 and s[j] != s[i]:
                        j = next[j - 1]
                    #已经退到模式串首位了,并且还是不匹配,这时候代表没有相等前后缀
                    if j == 0 and s[j] != s[i]:
                        next[i] = 0
                    #否则,都算是匹配上了,和最上面if情况一样做累计
                    else:
                        next[i] = j + 1
                        j += 1
            return next
        next = get_next(needle)
        j = 0
        for i in range(0, len(haystack)):
            if haystack[i] == needle[j]:
                j += 1
            else:
                while j > 0 and haystack[i] != needle[j]:
                    j = next[j - 1]
                if haystack[i] == needle[j]:
                    j += 1
            if j == len(needle):
                return i - len(needle) + 1
        return -1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值