美文网首页
[算法详解][KMP]Knuth–Morris–Pratt字符串

[算法详解][KMP]Knuth–Morris–Pratt字符串

作者: 奔跑的程序媛A | 来源:发表于2019-06-11 05:01 被阅读0次


    一个效率非常高的字符串匹配算法

    【基本思想】

    利用部分匹配表比较字符串S是否包含字符串P

    【步骤】

    算出一张《部分匹配表》(Partial Match Table)--P
    "部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。
    "前缀"指除了最后一个字符以外,一个字符串的全部头部组合
    "后缀"指除了第一个字符以外,一个字符串的全部尾部组合。
    1. 部分匹配表
    - "A"的前缀和后缀都为空集,共有元素的长度为0;
    - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
    - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
    - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
    - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
    - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
    - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

    A AB ABC ABCD ABCDA ABCDAB ABCDACD
    0 0 0 0 1 2 0

    2. next 数组
    next 数组考虑的是除当前字符外的最长相同前缀后缀。
    计算某个字符对应的 next 值,就是看这个字符之前的字符串中有多大长度的相同前缀后缀,使其向右移动一位,然后初始值赋为 -1。

    A B C D A B D
    A B C D A B D
    -1 0 0 0 0 1 2

    3. 遍历比较
    假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置

    • 如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j]),都令 i++,j++,继续匹配下一个字符;
    • 如果 j != -1,且当前字符匹配失败(即 S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P 相对于文本串 S 向右移动了 j - next [j] 位。
      • 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的 next 值,即移动的实际位数为:j - next[j],且此值大于等于1。

    【实例分析】


    S[10] 跟 P[6] 匹配失败。j = next[j],即 j 从 6 变到 2(字符 D 对应部分匹配表的值为0,对应的 next 值为 2)



    C 处再度失配
    j = next[j] = 0



    A 与空格失配,j = next[j] = -1。
    i++,j++

    【伪代码】

    假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置
    
    *   如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j]),都令 i++,j++,继续匹配下一个字符;
    *   如果 j != -1,且当前字符匹配失败(即 S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P 相对于文本串 S 向右移动了 j - next [j] 位。
    

    【代码实现】

    int KmpSearch(char* s, char* p)  
    {  
        int i = 0;  
        int j = 0;  
        int sLen = strlen(s);  
        int pLen = strlen(p);  
        while (i < sLen && j < pLen)  
        {  
            //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++      
            if (j == -1 || s[i] == p[j])  
            {  
                i++;  
                j++;  
            }  
            else  
            {  
                //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]      
                //next[j]即为j所对应的next值        
                j = next[j];  
            }  
        }  
        if (j == pLen)  
            return i - j;  
        else  
            return -1;  
    }  
    

    【性能分析】

    O(n+m)

    字符串匹配的KMP算法
    KMP 算法

    相关文章

      网友评论

          本文标题:[算法详解][KMP]Knuth–Morris–Pratt字符串

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