• 算法之美--3.2.3 KMP算法


    不知道看了几遍的kmp,反正到现在都没有弄清楚next[j]的计算和kmp的代码实现,温故而知新,经常回来看看,相信慢慢的就回了 

     从头到尾彻底理解KMP

    理解KMP

    /*!
    * file KMP_算法.cpp
    *
    * author ranjiewen
    * date 2017/02/12 16:12
    *
    */
    
    void preKmp(const char* pattern, int m, int kmpNext[])  //和GetNextval等价
    {
        int i, j;
        i = 0;
        j = kmpNext[0] = -1;
        while (i<m)
        {
            while (j>-1 && pattern[i] != pattern[j]) // i为后缀,j为前缀
            {
                j = kmpNext[j];
            }
            i++;
            j++;
            if (pattern[i] == pattern[j])
            {
                kmpNext[i] = kmpNext[j];
            }
            else
            {
                kmpNext[i] = j;
            }
        }
    }
    
    #include <iostream>
    using namespace std;
    #include <string>
    
    void KMP(string p, string t)
    {
        int m = p.length();
        int n = t.length();
        if (m>n)
        {
            cerr << "Unsuccessful match!";
        }
        const char* x = p.c_str();
        const char* y = t.c_str();
    
        int i = 0, j = 0, kmpNext[128];
        preKmp(x, m, kmpNext);
    
        i = j = 0;
        while (i<n)
        {
            while (j>-1 && x[j] != y[i])
            {
                j = kmpNext[j];
            }
            j++;
            i++;
            if (j >= m)
            {
                cout << "Matching index found at:" << i - j << endl;
                j = kmpNext[j];
            }
        }
    }
    int main(int argc, char** argv) {
    
        string p1 = "abcabcad";
        string p2 = "adcadcad";
        string p3 = "ababcaabc";
        string t = "ctcabcabcadtcaabcabcadat";
    
        cout << "KMP: " << endl;
        KMP(p1, t);
    
        //  cout<<"KMP: "<<endl;  
        //  KMP(p2, t);  
    
        //  cout<<"KMP: ";  
        //  KMP(p3, t);  
    
        return 0;
    }
    •  以后kmp算法都按照这样写
    void GetNext(char* p, int next[])
    {
        int pLen = strlen(p);
        next[0] = -1;
        int k = -1;
        int j = 0;
        while (j < pLen - 1) //j<pLen也没有错
        {
            //p[k]表示前缀,p[j]表示后缀  
            if (k == -1 || p[j] == p[k])
            {
                ++k;
                ++j;
                next[j] = k;  //对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相当于next[j] = k。
            }
            else //当不匹配时,更新新的前缀下标
            {
                k = next[k];
            }
        }
    }
    
    // 优化的地方
    //当p[j] != s[i] 时,下次匹配必然是p[next[j]] 跟s[i]匹配,如果p[j] = p[next[j]],必然导致后一步匹配失败(因为p[j]已经跟s[i]失配,
    //然后你还用跟p[j]等同的值p[next[j]]去跟s[i]匹配,很显然,必然失配),所以不能允许p[j] = p[next[j]]。
    //优化过后的next 数组求法  
    void GetNextval(char* p, int next[])
    {
        int pLen = strlen(p);
        next[0] = -1;
        int k = -1; //前缀
        int j = 0;
        while (j < pLen - 1)  //j<pLen也没有错
        {
            //p[k]表示前缀,p[j]表示后缀    
            if (k == -1 || p[j] == p[k])
            {
                ++j;
                ++k;
                //较之前next数组求法,改动在下面4行  
                if (p[j] != p[k])
                    next[j] = k;   //之前只有这一行  
                else
                    //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]  
                    next[j] = next[k];
            }
            else
            {
                k = next[k];
            }
        }
    }
    
    int KmpSearch(char* s, char* p)
    {
        int i = 0;
        int j = 0;
        int sLen = strlen(s);
        int pLen = strlen(p);
    
        int next[128] = { 0 };
        GetNextval(p,next);
    
        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; //此处i-j才是字符串开始匹配的地方
        else
            return -1;
    }

     有这样写的,else效果就是j==-1的时候

     while (Text[i] != '' && Pattern[j] != '')
        {
            if (Text[i] == Pattern[j])
            {
                ++i;// 继续比较后继字符
                ++j;
            }
            else
            {
                index += j - next[j];
                if (next[j] != -1)
                    j = next[j];// 模式串向右移动
                else
                {
                    j = 0;
                    ++i;
                }
            }
        }//while
  • 相关阅读:
    Font Awesome 中文网
    mobileselect学习
    JavaScript模块化
    webpack基本使用
    MVVM架构方式
    http-server开启测试服务器
    json-server模拟服务器API
    vue-router
    git的使用
    Vue生命周期
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/6391692.html
Copyright © 2020-2023  润新知