关于KMP算法(模块匹配)的实现

来源:互联网 发布:config.inc.php下载 编辑:程序博客网 时间:2024/05/24 20:08

这次我们来尝试实现以下模式匹配的一种改进算法,就是KMP算法,首先声明,本人是学生党一枚,智商有时捉急,算法写得只是仅供参考,因为可能有许多不对或者需要改进的地方,欢迎大佬们来指出,因为有些错可能会误导新手,所以还请小白慎看。好了,废话不多说,我们开始。

首先来看一个例子:

主串:a b c a b c a c

子串:a b c a c

设分别遍历主串与子串的指针为i,j(从0开始),由于KMP算法下i没有回溯,所以当出现值不相等时,需要在主串待匹配的位置i与子串中的某位继续比较。设这个子串中的位置位k。例子中k = 1,出现值不相等时i = 4,j = 4。可以看到k之前的串必须与i之前相同长度的一部分串相等(例子中该串只有一个值a),而主串中这个a在这次匹配中已经知道与子串第3位这个a相等。所以实际就是看子串j(出现值不相等的位置)前的一部分串(例子中为a b c a ),看这个串的前置串后置串相等的最大位数,像abca这样的 取最大相等前置串后置串结果就是a(1位),abcab这样的就是ab(2位)。那我就猜k这个值其实就等于这个位数。

根据上面连猜带懵得出的结论,匹配过程中发生值不相等时i位置再次与子串中要对比的位置k只与子串上述的这个最大相等串的位数有关。再总结一下对子串的操作:如果j = 0时匹配出现不等,从i + 1处继续与j = 0处匹配;如果 0<j<子串长度-1 位置出现不等,i 与子串中j前一部分串的最大相等串位数 的位置继续比较;j = 子串长度-1 时,匹配成功。需要计算出一个数组来存放不同位置出现匹配值不等时再次匹配的不同位置。

再分析一下这个数组,看书上起名叫next,它存放子串每个位的k值,从例子中看的话a是0,ab是0,abc是0,abca是1,还有一个空是-1(从一开始值就不相等)。next的长度和子串长度相等。

本来是想直接在一个函数里实现的,后来发现next数组的获取比想象中要复杂许多,我这大脑袋可不够转,试了又试终于懵出来一个差不多的函数,代码如下:

//创建next数组

int* GetNextArray(char* str)

{

    int* next = new int[strlen(str)];



    //赋初值

    for(int i = 0;str[i] != '\0';i++)

        next[i] = 0;

    next[0] = -1;



    bool equal = true;//判断本次前后置串是否相等



    for(int i = 0;str[i] != '\0';i++)//遍历子串

    {

        for(int k = 1;k<i;k++)//长度遍历

        {

            equal = true;

            for(int m = 0,n = i - k;n < i;m++,n++)//前后置串遍历

            {

                if(str[m] != str[n])

                    equal = false;

            }



            if(equal)

                next[i] = k;

        }

    }

    return next;

}

其实比较复杂的还是这一步next数组的获取,后面KMP算法的实现相对较简单,一步骤一步骤来就好,总的代码实现如下:

#include <iostream>

#include <cstring>

using namespace std;



char str[] = "abcabacbcabcaacbabcacbacb";

char str1[] = "abcac";


//创建next数组

int* GetNextArray(char* str)

{

    int* next = new int[strlen(str)];



    //赋初值

    for(int i = 0;str[i] != '\0';i++)

        next[i] = 0;

    next[0] = -1;


    bool equal = true;//判断本次前后置串是否相等


    for(int i = 0;str[i] != '\0';i++)//遍历子串

    {

        for(int k = 1;k<i;k++)//长度遍历

        {

            equal = true;

            for(int m = 0,n = i - k;n < i;m++,n++)//前后置串遍历

            {

                if(str[m] != str[n])

                    equal = false;

            }



            if(equal)

                next[i] = k;

        }

    }

    return next;

}



int KMP(char* str,char* str1)

{

    int* next = GetNextArray(str1);

    int i,j;

    for(i = 0,j = 0;j < (int)strlen(str1);i++,j++)

    {

        if(j == -1)//第一次比较就不同

        {

            j++;

            i++;

        }

        if(str[i] != str1[j])

        {

            j = next[j] - 1;

            i = i - 1;

            continue;

        }



    }

    cout<<"子串首次在"<<i - j<<"位置找到"<<endl;


    delete[] next;



    return 0;

}


int main()

{

    KMP(str,str1);


    return 0;

}

拿几个数据测试一下,没发现什么问题,好了,大功告成!

原创粉丝点击