《柔性字符串匹配》读书笔记(1)之--KMP算法(单模式串匹配、前缀匹配)

来源:互联网 发布:2017淘宝直通车 编辑:程序博客网 时间:2024/06/10 09:39
                                                                                                    ——by m4trix

《柔性字符串匹配》: 《Flexible Pattern Matching in Strings》
KMP算法: Knuth-Morris-Pratt algorithm
Knuth is Donald Knuth, you know the guy I mean.  


所谓字符串匹配:给出两个字符串A,B,回答B串是否是A串的子串(A串是否包含B串)。这里称A为文本,B为模式,即要回答模式B是否为文本A的子串。

有:
文本 A = t1t 2t3...t n
模式 B = p1p 2p3...p m
传统的字符串匹配算法:(算法时间复杂度O((n-m+1)*m))

        把文本A中位移分别0,1,...n-m时的文本(t1 t23...tm,  t 234...t m+1,  t 345...t m+2,  ...)依次跟模式B进行比较,查找匹配与否。

        这种算法没有对已匹配过的信息加以利用,KMP算法就是在充分利用已匹配信息的基础上,来避免一些明显的不合理的移位(每次当t xx+1x+2...t x+m-1与模式B不匹配时,需要对文本A后移一位t x+1x+2x+3...t x+m-2来继续进行对比,其实有的时候可以一次移z(z>1)位,这样子效率更优)。那么问题来了:KMP是怎么来对已匹配信息加以利用的呢,每次不匹配了需要对文本移位再比较时,移多少位呢,怎么确定:

KMP算法的思想:(算法最坏时间复杂度O(n),平均时间复杂度O(n))
一些概念:
        部分匹配表(Partial Match Table):所谓部分匹配表,即模式B中当匹配到第i个字符时,在p1 2p3 ...p i中,既是该串的前缀、同时又是该串的后缀的最长字符串的长度与索引i的一个映射关系表。
        如有模式串:"abababca"
        则有部分匹配表:
              
        如何计算得来:
                - "a"的前缀和后缀都为空集,因此公共元素的最大长度为0;
                - "ab"有前缀["a"], 后缀["b"], 公共元素的最大长度为0;
                - "aba"有前缀["a", "ab"], 后缀["ba", "a"], 公共元素"a"的最大长度为1;
                - "abab"有前缀["a", "ab", "aba"], 后缀["bab", "ab", "b"], 公共元素"ab"的最大长度为2;
                - "ababa"有前缀["a", "ab", "aba", "abab"], 后缀["baba", "aba", "ba", "a"], 公共元素"a", "aba"的最大长度为3;
                - "ababab"有前缀["a", "ab", "aba", "abab", "ababa"], 后缀["babab", "abab", "bab", "ab", "b"], 公共元素"ab", "abab"的最大长度为4;
                - "abababc"有前缀["a", "ab", "aba", "abab", "ababa", "ababab"], 后缀["bababc", "ababc", "babc", "abc", "bc", "c"], 公共元素的最大长度为0;
                - "abababca"有前缀["a", "ab", "aba", "abab", "ababa", "ababab", "abababc"], 后缀["bababca", "ababca", "babca", "abca", "bca", "ca", "a"], 公共元素"a"的长度为1;
        部分匹配长度(partial_match_length):所谓部分匹配长度,即文本A中的子串与模式B中的前i个字符相匹配,第i+1个字符不匹配,则此时的部分匹配长度为i.

有了部分匹配表和部分匹配长度,KMP算法对其怎么加以利用:
当文本A中的子串
部分匹配模式B时,有部分匹配长度:partial_match_length, 部分匹配表PMTable, 则当PMTable[partial_match_length] > 1, 则文本A向前位移 partial_match_length - PMTable[partial_match_length - 1]长度,继续用子串与模式B来匹配,依次循环,直到匹配上或者直至文本A走完为止。 

综上,KMP分两步来完成:
1、对模式串进行预处理,计算出部分匹配表;
2、依据Partial Match Table,对文本进行跳跃式位移来拿文本子串和模式进行匹配。

示例:
文本A:"bacbababaabcbab"
模式B:"abababca"

1、

如上图:
没有部分匹配中,文本A向前移1位;

2、

如上图:
部分匹配,有partial_match_length = 1,
根据:
              if  (PMTable[partial_match_length] > 1) {
                     文本A向前移partial_match_length - PMTable[partial_match_length - 1]长度;
              } else {
                      文本A向前移 1 位;
              }
PMTable[1] = 0 < 1,
文本A向前移1位;

3、

如上图:
没有部分匹配中,文本A向前移1位;

4、

如上图:
没有部分匹配中,文本A向前移1位;

5、

如上图:
部分匹配,有partial_match_length = 5,
PMTable[5] = 4 > 1,  partial_match_length - PMTable[partial_match_length - 1] = 5 -  PMTable[5 - 1] = 5 - 3 = 2,
文本A向前移2位;

6、

如上图:
部分匹配,有partial_match_length = 3,
PMTable[3] = 2 > 1,  partial_match_length - PMTable[partial_match_length - 1] = 3 -  PMTable[3 - 1] =3 - 1 = 2,
文本A向前移2位;

7、

如上图:
模式的长度已经超出了剩余的子文本串的长度了,因此匹配结束,没有匹配中。


C代码实现:
<span style="font-size:18px;">/* * input: x is the pattern string * input: m is the length of pattern string * output: KMPNext is the Partial Match Table */void preKMP(char *x, int m, int KMPNext[]){    int i, j;    i = 0;    j = KMPNext[0] = -1;    while (i < m) {        while (j > -1 && x[i] != x[j]) {            j = KMPNext[j];        }        i++;        j++;        if (x[i] == x[j]) {            KMPNext[i] = KMPNext[j];        } else {            KMPNext[i] = j;        }    }}void KMP(char *x, int m, char *y, int n){    int i, j, KMPNext[XSIZE];    /* Preprocessing */    preKMP(x, m, KMPNext);    /* Searching */    i = j = 0;    while (j < n) {        while (i > -1 && x[i] != y[j]) {            i = KMPNext[i];        }        i++;        j++;        if (i >= m) {            //OUTPUT(j - i);            i = KMPNext[i];        }    }}</span>

KMP算法的适用场景:
因为KMP算法是对模式串进行预处理,因此该算法非常适合求解这样的问题:
给定一个模式串B,和一群文本A串,问B是哪些A串的子串。



Refer:
"The Knuth-Morris-Pratt Algorithm in my own words"
http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/

"KMP算法详解"
http://blog.csdn.net/yutianzuijin/article/details/11954939

"Knuth-Morris-Pratt algorithm description and C code by Christian Charras and Thierry Lecroq"
http://www-igm.univ-mlv.fr/~lecroq/string/node8.html

0 0