zoukankan      html  css  js  c++  java
  • KMP算法

    KMP算法比较抽象,第一次接触的人往往晦涩难懂,博主也是前前后后好几次思索才有了些许的思路,故在此及时记录,防备遗忘。
    KMP算法对于朴素匹配算法的改进是引入了一个跳转表next[]。以模式字符串abcabcacab为例,其跳转表为:

    j 1 2 3 4 5 6 7 8 9 10
    pattern[j] a b c a b c a c a b
    next[j] 0 1 1 0 1 1 0 5 0 1
    跳转表的用途是,当目标串target中的某个子部target[m…m+(i-1)]与pattern串的前i个字符pattern[1…i]相匹配时,如果target[m+i]与pattern[i+1]匹配失败,程序不会像朴素匹配算法那样,将pattern[1]与target[m+1]对其,然后由target[m+1]向后逐一进行匹配,而是会将模式串向后移动i+1 - next[i+1]个字符,使得pattern[next[i+1]]与target[m+i]对齐,然后再由target[m+i]向后与依次执行匹配。
    匹配过程如下:
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
    b a b c b a b c a b c a a b c a b c a b c a c a b c
    a b c a b c a c a b
    a b c a b c a c a b
    a b c a b c a c a b
    a b c a b c a c a b
    a b c a b c a c a b
    a b c a b c a c a b
    next跳转表,在进行模式匹配,实现模式串向后移动的过程中,发挥了重要作用。这个表看似神奇,实际从原理上讲并不复杂,对于模式串而言,其前缀字符串,有可能也是模式串中的非前缀子串,这个问题我称之为前缀包含问题。以模式串abcabcacab为例,其前缀4 abca,正好也是模式串的一个子串abc(abca)cab,所以当目标串与模式串执行匹配的过程中,如果直到第8个字符才匹配失败,同时也意味着目标串当前字符之前的4个字符,与模式串的前4个字符是相同的,所以当模式串向后移动的时候,可以直接将模式串的第5个字符与当前字符对齐,执行比较,这样就实现了模式串一次性向前跳跃多个字符。所以next表的关键就是解决模式串的前缀包含。当然为了保证程序的正确性,对于next表的值,还有一些限制条件,后面会逐一说明。
    跳转表的建立
    `inline void BuildNext(const char* pattern, size_t length, unsigned int* next)
    {
    unsigned int i, t;

    i = 1;  
    t = 0;  
    next[1] = 0;  
    
    while(i < length + 1)  
    {  
        while(t > 0 && pattern[i - 1] != pattern[t - 1])  
        {  
            t = next[t];  
        }  
    
        ++t;  
        ++i;  
    
        if(pattern[i - 1] == pattern[t - 1])  
        {  
            next[i] = next[t];  
        }  
        else  
        {  
            next[i] = t;  
        }  
    }  
    
    //pattern末尾的结束符控制,用于寻找目标字符串中的所有匹配结果用  
    while(t > 0 && pattern[i - 1] != pattern[t - 1])  
    {  
        t = next[t];  
    }  
    
    ++t;  
    ++i;  
    
    next[i] = t;  
    

    } `
    利用跳转表实现字符串匹配的算法如下:

    unsigned int KMP(const char* text, size_t text_length, const char* pattern, size_t pattern_length, unsigned int* matches)  
    {  
        unsigned int i, j, n;  
        unsigned int next[pattern_length + 2];  
    
        BuildNext(pattern, pattern_length, next);  
    
        i = 0;  
        j = 1;  
        n = 0;  
    
        while(pattern_length + 1 - j <= text_length - i)  
        {  
            if(text[i] == pattern[j - 1])  
            {  
                ++i;  
                ++j;  
    
                //发现匹配结果,将匹配子串的位置,加入结果  
                if(j == pattern_length + 1)  
                {  
                    matches[n++] = i - pattern_length;  
                    j = next[j];  
                }  
            }  
            else  
            {  
                j = next[j];  
    
                if(j == 0)  
                {  
                    ++i;  
                    ++j;  
                }  
            }  
        }  
    
        //返回发现的匹配数  
        return n;  
    }  

    【参考文献】
    《Introduction to Algorithms》Second Edition

    by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford .

    推荐 “Bill_Hoo专栏” 博客,更多详细内容,可阅读http://billhoo.blog.51cto.com/2337751/411486

  • 相关阅读:
    快排笔记C++
    security+redis+jwt 一个登陆注册查询的例子
    centeros 配置好环境虚拟机下载(java git mysql maven nginx Python redis nodejs tomcat )
    ZwQueryInformationProcess 反调试代码
    c++ 创建进程设置窗口标题模拟键盘鼠标例子
    求一个数二进制中包含多少个1
    憨批是我
    憨批是我
    问卷星实现自动填表刷问卷(问卷星分析post协议实现 安卓版)
    前端面试题 -- 综合
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9387476.html
Copyright © 2011-2022 走看看