美文网首页
字符串查找匹配--kmp算法

字符串查找匹配--kmp算法

作者: 凉初透的风 | 来源:发表于2018-10-09 16:20 被阅读0次

    部分匹配表

    KMP的关键是部分匹配表。理解KMP的主要障碍是没有完全掌握部分匹配表中的值到底意味着什么。试着用最简单的话来解释它们。

    这是“abababca”模式的部分匹配表:

    char:  | a | b | a | b | a | b | c | a |
    index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
    value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |
    

    现在,为了谈论其含义,我们需要了解正确的前缀和正确的后缀。

    正确的前缀:

    字符串中的所有字符,其中一个或多个字符被截断。
    “S”,“Sn”,“Sna”和“Snap”都是“Snape”的正确前缀。
    截取的范围是从字符串的第一个字符到倒数第二个字符。

    正确的后缀:

    字符串中的所有字符,一个或多个字符在开头处截断。
    “agrid”,“grid”,“rid”,“id”和“d”都是“Hagrid”的正确后缀。
    截取的范围是从字符串的第二个字符到倒数第一个字符。

    考虑到这一点,我现在可以给出部分匹配表中值的一句话含义:

    (子)模式中与同一(子)模式中的正确后缀匹配的最长正确前缀的长度。

    现在来分析“abababca”这个模式串:

    对“a”分析,只有一个字符串,没有前缀和后缀,故值为0。

    对“ab”分析,有一个正确的前缀(“a”)和一个正确的后缀(“b”),前缀和后缀不匹配,故值为0。

    对“aba”分析,有两个正确的前缀(“a”和“ab”)和两个正确的后缀(“a”和“ba”)。正确的前缀“ab”与两个正确的后缀中的任何一个都不匹配。但是,正确的前缀“a”与正确的后缀“a”匹配。因此,在这种情况下,匹配正确后缀的最长正确前缀的长度是1。

    对四个字符(“abab”)分析,有三个正确的前缀(“a”,“ab”和“aba”)和三个正确的后缀(“b”,“ab”和“bab”)。“ab”在两者中,并且是两个字符长,因此值为2。

    对“ababa”分析,有四个正确的前缀(“a”,“ab”,“aba”和“abab”)和四个正确的后缀(“a”,“ba”,“aba”和“baba”)。现在,有两个匹配:“a”和“aba”都是正确的前缀和正确的后缀。由于“aba”比“a”长,所以它获胜,因此值为3。

    对“abababc”分析。即使没有列举所有正确的前缀和后缀,显然也不会有任何匹配; 所有后缀都以字母“c”结尾,并且没有前缀与之匹配,因此值为0。

    对“abababca”分析,由于它们都以“a”开头和结尾,我们知道它的值至少为1.但是,它就是它的结束点; 在长度为2或更高时,所有后缀都包含ac,而只有最长的前缀(“abababc”)包含“c”。这个七个字符的前缀与七个字符的后缀(“bababca”)不匹配,因此值为1。

    如何使用部分匹配表

    当我们找到部分匹配时,我们可以使用部分匹配表中的值来跳过(而不是重做不必要的旧比较)。该公式的工作方式如下:

    如果找到长度为partial_match_length的部分匹配table[partial_match_length] > 1,我们可以跳过前面的partial_match_length - table[partial_match_length - 1]字符。

    假设我们将“abababca”模式与“bacbababaabcbab”相匹配。这里是我们的部分匹配表,以便于参考:

    char:  | a | b | a | b | a | b | c | a |
    index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
    value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |
    

    我们第一次获得部分匹配是在这里:

    bacbababaabcbab
     |
     abababca
    

    这是partial_match_length为1. table[partial_match_length - 1](或table[0])的值为0,因此我们不会先跳过任何一个。我们得到的下一个部分匹配是:

    bacbababaabcbab
        |||||
        abababca
    

    这是partial_match_length为5. table[partial_match_length - 1](或table[4])的值为3.这意味着我们可以跳过前面partial_match_length - table[partial_match_length - 1](或5 - table[4]或5 - 3或2)字符:

    // x denotes a skip
    
    bacbababaabcbab
        xx|||
          abababca
    

    这是partial_match_length 3. table[partial_match_length - 1](或table[2])的值是1.这意味着我们可以跳过前面partial_match_length - table[partial_match_length - 1](或3 - table[2]或3 - 1或2)字符:

    // x denotes a skip
    
    bacbababaabcbab
          xx|
            abababca
    

    代码实现

    
    def makeNext(pattern):
        """
        求出模式串的最大前后缀长度数组
        :param pattern: 模式串
        :return:
        """
        # 最大前后缀长度
        k = 0
        # 模版字符串长度
        m = len(pattern)
        next = [0] * (m-1)
        # 模版字符串的第一个字符的最大前后缀长度为0
        next.append(0)
    
        # 从第二个字符开始,依次计算每一个字符对应的next值
        for q in range(1, m):
            # 求出P[0]···P[q]的最大的相同的前后缀长度k
            while k > 0 and pattern[q] != pattern[k]:
                k = next[k-1]
            # 如果相等,那么最大相同前后缀长度加1
            if pattern[q] == pattern[k]:
                k = k + 1
            next[q] = k
    
        return next
    
    def kmp(target, pattern):
        """
        在目标串里查找模式串,成功查找后返回目标串中模式串出现位置的第一个下标
        :param target: 目标串
        :param pattern: 模式串
        :return:
        """
        # 目标串下标,不回溯
        q = 0
        # 目标串的长度
        n = len(target)
        # 模式串的长度
        m = len(pattern)
        # 求取模式串的匹配串数组
        next = makeNext(pattern)
        for i in range(1, n):
            while q > 0 and pattern[q] != target[i]:
                q = next[q-1]
            # 模式串中的字符与目标串的字符相等,q向后移一位
            if pattern[q] == target[i]:
                q = q + 1
            #
            if q == m:
                return i - m + 1
    
    
    
    if __name__ == '__main__':
        t = 'ACBDCBADAABCDABCA'
        p = 'ABCDABC'
        print(t)
        print(p)
        # next = makeNext(p)
        result = kmp(t, p)
        print(result)
    
    

    相关文章

      网友评论

          本文标题:字符串查找匹配--kmp算法

          本文链接:https://www.haomeiwen.com/subject/jhcyaftx.html