字符串匹配KMP算法

2018/02/19 Algorithms 共 1792 字,约 6 分钟

image-20210123112420296

next数组的意思就是 next[i] = len; 长度为i的数组的前缀和后缀相等的最大长度。 例如abcdabc就是 next[7] = 3; 相等的前缀和后缀最长是abc长度为3

next理解: KMP中的next数组,有的也称fail失败数组,存的到底是什么。是首子串和尾子串能匹配的最大长度

kmp

特意设置next[0] = -1, 因为当next[0]=0地时候会死循环,无限地j=next[j], j=0.

class Solution:
    def getNext(self, s):
        next = [-1 for _ in range(len(s))]
        # 初始状态,i=0,j=-1,相等或者j==-1即+1,否则j回退。index 1的值0,index 0的值是-1.
        i, j = 0, -1
        while i < len(s) - 1:
            if j == -1 or s[i] == s[j]:
                i, j = i + 1, j + 1
                next[i] = j
            else:
                j = next[j]
        return next

    def strStr(self, haystack: str, needle: str) -> int:
    		# 初始状态,i和j都是0,相等或者j==-1即+1,否则j回退。
        if len(needle) == "":
            return 0
        i, j = 0, 0
        next = self.getNext(needle)
        while i < len(haystack) and j < len(needle):
            if j == -1 or haystack[i] == needle[j]:
                i, j = i + 1, j + 1
            else:
                j = next[j]
        return i - j if j == len(needle) else -1

有p[j]==p[next[j]]的优化

参考

def kmp_search(s=str(), p=str()):
    next = get_next(p)
    i, j = 0, 0
    while i < len(s) and j < len(p):
        if j == -1 or s[i] == p[j]:
            i, j = i + 1, j + 1
        else:
            j = next[j]
    if j == len(p):
        return i - j
    else:
        return -1


def get_next(p=str()):
    next = [0] * len(p)
    next[0] = -1
    i, j = 0, -1
    while i < len(p) - 1:
        if j == -1 or (p[i] == p[j]):
            i, j = i + 1, j + 1
            if p[i] != p[j]:
                next[i] = j
            else:
                next[i] = next[j]
        else:
            j = next[j]
    return next


if __name__ == '__main__':
    s = 'abcdabcefgsabc'
    # print get_next(s)
    print kmp_search(s, "abcefg")

求next数组

next数组的意思就是 next[i] = len; 长度为i的数组的前缀和后缀相等的最大长度。 例如abcdabc就是 next[7] = 3; 相等的前缀和后缀最长是abc长度为3。

因此这里是先i++,j++,再赋值next,因为kmp用next地时候是if s[i] != p[j], j = next[j].

    def getNext(self, s):
        next = [-1 for _ in range(len(s))]
        i, j = 0, -1
        while i < len(s) - 1:
            if j == -1 or s[i] == s[j]:
                i, j = i + 1, j + 1
                next[i] = j
            else:
                j = next[j]
        return next

扩展kmp

拓展kmp是对KMP算法的扩展,它解决如下问题:

  • 问s串与t串的每一个后缀的最长公共前缀

img

强化条件:当s与t完全相同时

先尝试通过强化条件,降低一下难度:假如s与t完全相同,扩展kmp该如何实现?

当s与t完全相同,其实问题可以进一步转化为“s与s的每一个后缀的最长公共前缀”。

参考: https://oi-wiki.org/string/z-func/

# Python Version
def z_function(s):
    n = len(s)
    z = [0] * n
    l, r = 0, 0
    for i in range(1, n):
        if i <= r and z[i - l] < r - i + 1:
            z[i] = z[i - l]
        else:
            z[i] = max(0, r - i + 1)
            while i + z[i] < n and s[z[i]] == s[i + z[i]]:
                z[i] += 1
        if i + z[i] - 1 > r:
            l = i
            r = i + z[i] - 1
    return z

Search

    Table of Contents