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

    算法背景

    KMP算法解决的问题是字符匹配,是由Knuth–Morris–Pratt共同开发出来的,这个算法把字符匹配的时间复杂度缩小到O(m+n),而空间复杂度也只有O(m),n是target的长度,m是pattern的长度,在此算法在发明之前并不是没有如此高效的算法,但是原算法比较复杂。Kmp算法优雅高效,但是实现却不难理解且代码长度很短,是优秀算法设计的典范,值得拿出来仔细分析;

    算法核心

    在最简单的枚举移动算法中,我们可以看到;逐步匹配的复杂度是O(mn),正好是两层嵌套;但是KMP算法的复杂度只有O(m+n) 这个是怎么做到的呢;主要是利用到了一个跳转表,也就是一个自我覆盖的表达形式;例如 字符串stringnapaspace ,space 进行匹配,当在前面的位置没匹配到的时候,它不会弱智得还会移动一个单位 patterntarget对照匹配,跳转表,就是挑战,因为它本身已经实现了自己的跳转,当自己匹配不到的时候,它就知道匹配接下来的几个小位移都是没有作用的;所以直接移动 next[j]个位置。

    重点解疑

    常见的问题是为什么跳转表能实现这个功能,为什么这么准确,它到底怎么实现的,还可以怎么理解。
    关于这个问题,可以自己模拟一下去体会,可以发现这个规律。

    注意事项

    数学的来看一下定义,比如对于序列
    待自我匹配序列
    而没有更大的k满足这个条件,就是说要找到尽可能大k,使pattern前k字符与后k字符相匹配,k要尽可能的大,原因是如果有比较大的k存在,而我们选择较小的满足条件的k,那么当失配时,我们就会使pattern向右移动的位置变大,而较少的移动位置是存在匹配的,这样我们就会把可能匹配的结果丢失.
    这里写图片描述
    计算这个跳转表的方法可以采用递推,可以想象如果对于pattern的前j个字符,如果覆盖函数值为k
    则对于pattern的前j+1序列字符,则有如下可能:

    ⑴ pattern[k+1]==pattern[j+1] 此时overlay(j+1)=k+1=overlay(j)+1

    ⑵ pattern[k+1]≠pattern[j+1] 此时只能在pattern前k+1个子符组所的子串中找到相应的next函数,h=next(k),如果此时pattern[h+1]==pattern[j+1],则next(j+1)=h+1否则重复(2)过程.

    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;
    }

    跳转表的工作

    假如:A=”abababaababacb”,B=”ababacb”,我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。

    直接实现

    KMP理解了跳转表,基本上这个过程就很前清晰了;后期关于图示法描述出来。

    程序代码

    #include<iostream>
    #include<string>
    #include<vector>
    using namespace std;
    
    int kmp_find(const string& target,const string& pattern){
        const int target_length = target.size();
        const int pattern_length = pattern.size();
        int * overlay_value = new int[pattern_length];
        overlay_value[0] = -1;
        int index = 0;
        for(int i=1;i<pattern_length;++i){
            index = overlay_value[i-1];
            while(index>=0 && pattern[index+1]!=pattern[i])
                index  = overlay_value[index];
            if(pattern[index+1]==pattern[i])
                overlay_value[i] = index +1;
            else
                overlay_value[i] = -1;
        }
        //KMP match algorithm really start ; up is for next[]
        int pattern_index = 0;
        int target_index = 0;
        while(pattern_index<pattern_length&&target_index<target_length){
            if(target[target_index]==pattern[pattern_index]) {
                ++target_index;
                ++pattern_index;
            }
            else if(pattern_index==0)
                ++target_index;
            else
                pattern_index = overlay_value[pattern_index-1]+1;
        }
    
        if(pattern_index==pattern_length)
            return target_index-pattern_index;
        else
            return -1;
    }
    
    int main(){
        string source = "abcdefghijklmn";
        string pattern = "cdef";
        cout<<kmp_find(source,pattern)<<endl;
        return 0;
    }
    

    参考文章

    KMP算法详解|martix67

    从头到位理解KMP算法

    KMP算法详解

  • 相关阅读:
    向上造型
    Service与Activity通信
    adb报错:no permissions (user in plugdev group; are your udev rules wrong?); see [http://developer.android.com/tools/device.html]
    13-AB Test学习
    1-端到端机器学习项目
    12-数据分析框架
    2-电影推荐案例学习
    11-pyecharts使用Tab不完全代码示例
    22-基于Python构建GRPC服务
    akka练习
  • 原文地址:https://www.cnblogs.com/actanble/p/6713439.html
Copyright © 2011-2022 走看看