我也学算法 - KMP算法

一直以来,对算法都是理论大于实际,甚至没有实际.

最近由于项目需要.从新了解了一下KMP算法.唉,讨厌这种被动的学习过程.

不过KMP算法还是很有意思的,用了两天的时间才总算是弄懂了.期间参考了网上的博文和数据结构.下面分享一下KMP算法的心得.

KMP的总体思想是利用模式串本身的特性来优化匹配的步骤.如何利用自身的特性呢,KMP借助一个数组来实现,也就是大多数教程中提到的next数组.后面我会介绍next数组是如何构建和使用的.

前面提到KMP算法需要模式串满足一定的条件,那么这个条件是什么呢.这里直接引用数据结构书中的等式:

当k < j 时,  t1t2…tk-1tk = tj-(k-1)tj-k… tj-1 tj.

如果没有这个等式,那么KMP算法无异于浪费了next大小的空间的最普通的字符串匹配算法.


下面用一张图来描述一下这个等式在匹配子串的时候起到的作用:

(本图片假定数组下标从1开始)首先我们找到等式: t1t2 = t6t7.所以当t8不匹配的时候,KMP算法会自动对齐t1 t2 然后用t3和母串进行匹配.


那么根据以上的分析,我们可以得出以下的几个结论(可能得出的有点仓促,同学们还是需要结合课本来进行理解)

1. next数组和模式串的下标是一一对应的.

2. 每个字符对应的next保存的是其前一个字符匹配的前缀的下标.从上面的图中可以看到next[8]保存的是3.

3. 第一个字母没有前缀,所以它的next保存的是一个无效的下标.在实际编程中,可以是-1

4. 如果一个字符的前面没有满足等式的子串,则其next保存的是模式串的首字母的下标.


根据我们的结论,下面给出KMP算法的实现:

void kmp_get_next(char *pattern, int next[])
{
    int i = 0, j = 0;

    next[i] = -1;
    
    while (i < strlen(pattern) - 1)
    {
        if (j == i || pattern[i] == pattern[j])
        {
            if (j != i)
            {
                j++;
            }

            i++;
            
            next[i] = j;
        }
        else if (j != 0)
        {
            j = next[j];
        }
        else
        {
            i++;
        }
    }
}

void kmp_print_next(char *pattern, int next[])
{
    int i = 0; 

    if (NULL == pattern)
    {
        return;
    }

    printf("%s 's next array is : \n", pattern);
    for (i = 0; i < strlen(pattern); i++)
    {
        printf("%d\t", next[i]);
    }
}

int kmp_is_match(char *pattern, char *basestr, int next[])
{
    int i = 0; 
    int j = 0; 

    while (i < strlen(basestr))
    {
        if (basestr[i] == basestr[j])
        {
            if (j == strlen(pattern))
            {
                return 0;
            }

            i++;
            j++;

        }
        else
        {
            j = next[j];
            if (j == -1)
            {
                i++;
            }
        }
    }

    return -1;
}

int kmp_get_match(char *pattern, char *basestr, int next[])
{
    int i = 0; 
    int j = 0; 

    while (i < strlen(basestr))
    {
        if (basestr[i] == basestr[j])
        {
            if (j == strlen(pattern))
            {
                return i;
            }

            i++;
            j++;
        }
        else
        {
            j = next[j];
            if (j == -1)
            {
                i++;
            }
        }
    }

    return -1;
}

#include "kmp.h"

int main(int argc, char **argv)
{
    int ret = 0;
    int i = 0;
    char *pattern = "abaabc";
    int next[10] = {0};

    char *basestr = "abcabcabcabccacabdabaabcabceadb";

    kmp_get_next(pattern, next);

    printf("%s matched\n", kmp_is_match(pattern, basestr, next) == 0 ? "is" : "not");

    ret = kmp_get_match(pattern, basestr, next);

    return 0;
}

KMP算法其实理解起来并不难,比较难的就是构造next数组.至少对我而言是这样.一旦数组构造出来,那剩下的就都好办了.

可能是我理解的还不够深把,写之前觉得有很多内容可以分享,但是开始写以后发现不知道该写什么.希望能够对同学们有所启发.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值