算法 - KMP字符串匹配及其Python实现

KMP模式匹配算法

1.思路分析

参考:https://blog.csdn.net/lemon_tree12138/article/details/48488813

1.1 知识补充

匹配模式中,每个最优前缀(关于最优前缀可以参考《算法导论》32章内容)S中,S的不为自身的最长的一个等于最优后缀(关于最优后缀可以参考《算法导论》32章内容)的最优前缀SS。这句话可能听起来有一些绕口,下面通过一个实例来说明:

匹配模式P:ababaca

我们选取P的一个最优前缀S = ababa,那么SS = aba.因为SS是S的最优前缀,也是S的最优后缀,而且是最长的。

1.2 例子解释

KMP算法的关键是为我们排除了一些重复匹配,使用主字符串的匹配位置“指针”不需要回溯。这里不妨列举一个小例子。

主字符串T:fababadaaswababaca
匹配模式P:ababaca

假使此时我们正在匹配T的第7位(fababa[d]aaswababaca)和P的第6位(ababa[c]a),而且匹配失败了。针对朴素的匹配模式是T的“指针”回溯到(fa[b]abadaaswababaca),P的“指针”回溯到([a]babaca)。
T“指针”的回溯无疑是浪费了很多的时间。

我们重新检查一下P(ababaca),当我们开始匹配第6位的时候,之前的5位已经匹配完成。而且,[aba]ba= ab[aba]。
那么针对于T而言,fab[aba]daaswababaca这三位是已经匹配过了,匹配的是P(ab[aba]ca),并且[aba]ba= ab[aba]。
可以这样理解,这里选取的最优子串可以代表整个当前匹配字符串T的一部分(ababa),在匹配时,一旦前缀失效,则直接从后缀开始匹配。
这时,我们可以直接将T“指针”直接指向fab[aba]后面一个元素(即i=6,同时,P“指针”指向[aba]后面一个元素(即i=3)

2 代码实现

import time
import numpy as np


class SimpleMatching():

    def getIndexOfPinT(self, t, p):
        if t is None or p is None:
            return None

        indexes = []

        for i in range(len(t) - len(p) + 1):
            for j in range(len(p)):
                if t[i + j] == p[j]:
                    if j == len(p) - 1:
                        indexes.append(i)
                    continue
                break

        return indexes


class KmpMatching():

    def getNext(self, text):
        if text is None:
            return None

        lengths = np.zeros(len(text), dtype=int)
        for i in range(len(text)):
            sub = text[0: i+1]
            maxlen = 0
            for j in range(len(sub)-1):
                subChild = sub[0: j+1]
                if sub.endswith(subChild) and len(subChild)>maxlen:
                    maxlen = len(subChild)

            lengths[i] = maxlen

        return lengths

    def getIndexOfPinT(self, t, p):
        if t is None or p is None:
            return None
        indexes = []
        next = self.getNext(p)

        indexT = 0
        indexP = 0
        while indexT < len(t):
            if t[indexT] == p[indexP]:
                indexP += 1
                indexT += 1
            elif indexP == 0:
                indexT += 1
            else:
                indexP = next[indexP - 1]

            if indexP == len(p):
                indexes.append(indexT - indexP)
                indexP = 0

        return indexes


if __name__ == "__main__":
	
	t = "fababadaaswababaca"*100000
    p = "ababaca"
    
    start_SimpleMatch = time.time()
    simple_match = SimpleMatching()
    indexes1 = simple_match.getIndexOfPinT(t, p)
    print(indexes1)
    end_SimpleMatch = time.time()
    print("The time of SimpleMatching is: ", end_SimpleMatch-start_SimpleMatch)

    start_KMPMatch = time.time()
    KMP_match = KmpMatching()
    indexes2 = KMP_match.getIndexOfPinT(t, p)
    print(indexes2)
    end_KMPMatch = time.time()
    print("The time of KMP-Matching is: ", end_KMPMatch - start_KMPMatch)


>>> The time of SimpleMatching is:  1.203505516052246
	The time of KMP-Matching is:  1.116056203842163

从结果可以看出,KMP算法比朴素匹配速度更快

你可能感兴趣的:(算法)