KMP实现与失效函数的改进(C++)

朴素算法的思想最好理解当相同时往后匹配,不同时就再从第一个开始重新匹配,对于无规则乱序还可以,但是如果主串为“abacba”,模式串中如“abab”,我们可以发现当abab后一个匹配失败时朴素算法需要重新从主串的第二个“b”开始与匹配串重新比较,但是KMP就改进了这个缺点,我们已经知道了两个串前三个一样,那为什么不从主串的c与匹配串的第一个b比较,那主串的c就是主串匹配失败的位置,匹配串就是匹配失败时当前位置的前一个的真子串长度,因为匹配失败前都已经匹配成功,而只要知道前面的真子串长度。因为是从0开始记录所以就是真子串的长度而不用加一。
所以我们大概了解了KMP算法:如果当前字符匹配成功就两个都往后移,如果失败,主串位置不变,匹配串返回当前位置前(不含当前字符)真子串的长度。
失效函数也就是求返回当前位置前(不含当前字符)真子串的长度,那么也可以看成是同样使用KMP算法来求。我们令第1个f[0]的为-1

void GetFailure(const String &pat, int f[])
//求匹配串pat的失效函数值
{
    f[0]=-1;                             // 初始f[0]的值为-1
    int j = 0, k = -1;      
    while (j < pat.GetLength() - 1)
        if (k == -1 || pat[k] == pat[j])    
            f[++j]=++k;// f[1]=0,如果相同就等于原子串长度+1
        else                             // pat[k]与pat[j]不匹配
            k = f[k];                    // 寻求新的匹配字符
}

那么如果失效函数为求出的每位真子串长度为F[];
那么KMP算法的主体部分就是

int KMP_find(const String &ob, const String &pat, int p = 0)
// pat匹配串,ob主串,主串第p个位置开始匹配
{
    int *f = new int[pat.GetLength()];      // 为失效函数值数组f分配空间
    GetFailure(pat, f);                  // 通过失效函数求每位真子串
    int i = p, j = 0;           
    while (i < ob.GetLength() && j < pat.GetLength() && pat.GetLength() - j <= ob.GetLength() - i)
//i记录主串当前匹配到的位置,j记录主串当前匹配到的位置,如果主串剩下的少于匹配串剩下的就结束
    if (j == -1 || pat[j] == ob[i])     {
        i++; j++;                        // 模式串pat与目标串ob的当前位置向后移
    }
    else                                 // pat[j]与ob[i]不匹配
        j = f[j];                        // 寻找新的模式串pat的匹配字符位置
    delete []f;                          // 释f所占用的存储空间
    if (j < pat.GetLength())
        return -1;                       // 匹配失败
    else
        return i - j;                    // 匹配成功
}

现在我们发现KMP算法貌似已经很不错了,但是假如有一个匹配串abcab,主串abcad…,如果直接使用KMP。我们会发现第二段匹配时要ab与ad比较,但是如果只看匹配串,因为前面和后面都是ab,所以没有意义再比较一遍这时候就要改进失效函数,多加一条判断,当前返回的pat[f[4]]== pat[4],如果相同让他们的f[]相同,这样就像是跳过了这段无意义的比较。

void GetFailure(const String &pat, int f[]){
        f[0]=-1;                             // 初始f[0]的值为-1
        int j=0,k = -1;
    while (j < pat.GetLength() - 1)
            if(k == -1 || pat[k] == pat[j]){
              f[++j]=++k;

                if (pat[k]==pat[j]) {  
                    f[j]=f[k];    //f[]相同 
                    } 
                else {  
                    f[j]=k;  //不变
                } 
            }
            else
                k = f[k];

        }
    原文作者:KMP算法
    原文地址: https://blog.csdn.net/BEINGOC/article/details/54292940
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞