ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

简单易懂的KMP算法理解

2019-08-15 19:01:01  阅读:198  来源: 互联网

标签:字符 匹配 大串 后缀 长度 算法 KMP 易懂 模板


KMP算法是由三大佬研发出来的用于匹配字符串的算法。

解决在一个大串中找与模板串相同的子串。

解决上诉问题,有一个简单且好理解的方式:

将大串前(M-N+1)个字符(M代表大串长度,N代表模板串的长度)依次与模板串的第一个字符比较,不匹配则大串当前比较的字符不是模板串的第一个字符则需要匹配下一个大串字符,若匹配,则大串当前比较的字符是模板串的第一个字符则需要将大串当前字符的下一个字符和模板串的下一个字符进行比较,依次类推,直到匹配全找到子串或者找不到。

这样的方法优点是简单好实现,但是显然会花费许多时间,举一个最坏情况下的例子:

大串:aaaaaaaaaaaaaaaaaaaab

模板串:aaaaab;

这个例子在最后一次匹配才成功找到,而前面的每次匹配都将模板串的每个字符都遍历了一次,如果大串长度M,模板串长度N,那么花费的时间共为O((M-N+1)*N);数量级可看成为二次方级的。在数据量稍微大些的情况下会很慢。

 

 

KMP算法便是用来解决这种缺点的。

KMP算法关键之处其实就是记录模板串中的前缀子串中的相同的最长前、后缀的长度。这样如果模板串中途匹配失败后,模板串匹配大串后移将不只是一位。而是移动直至匹配失败前的前缀子串(这里用x代替)中的前缀和其(x)后缀重叠,这样移动的位数为两相同的前后缀位置差。如果这样做是正确的,不就是节省了一些时间吗?那么接下来就解释下为什么这样做是正确的:

如果一个长度为N模板串配成功后,向后移动n位前N-n位依然能匹配对,那么会有什么性质?从第一次匹配最后一个字符往前算起长度N-n的子串是什么?其实就是模板长度为N-n的前后缀(因为两次都与大串匹配对了,这两个前后缀显然是相等的)。反过来说,若是一个模板串有多组相等的前后缀,如有三组长度分别为N-n1,N-n2,N-n3,那么模板串匹配成功后向后分别移n1,n2,n3个位置后依然有N-n1,N-n2,N-n3位能全匹配。

把这个应用到模板串匹配失败时候的处理:将匹配失败前的前缀子串(x)看成上述讲的成功匹配的小模板串,这个模板串可以根据已有的(如果有)相同前后缀的长度移动,为保证不漏匹配,只需要取最长前后缀(这样可以使得移动最小)的长度决定移动位数,即可保证这种匹配方法的正确性。

上述便是KMP的一个重点所在。

 

 

还有一个重点便是如何比较快的找到模板串前缀子串中的相同最大前后缀。这个也是KMP的难点:

事实上,如果模板串很长的话,要找到它的最长相同前后缀其实也不省事。若模板串短显然就容易了,那么不妨找个几个例子看看短模板串前后缀:

abca

显然最长前后缀为a

abcab

最长前后缀为ab

abcabc

最长前后缀为abc

上面三个例子中的下两个是由第一个例子增加一个字符扩展而成的,这样做的目的是展示一种情况:如果一个字符串最长相同前后缀(这里用cnext[n-1],n-1代表前缀最后一个字符的数组下标)长度为n,那么该字符串增加一个字符后最大前后缀最长上限是n+1。而达到这个上限的条件是增加的字符和原字符串的cnext[n-1]的下一个字符相同,这种匹配成功的情况下便是只匹配一次字符就可以得出最大前后缀的长度。但事实上更多情况是匹配失败,如果匹配失败该怎么做?举一个例子:

abacabab

abacaba这个最长相同前后缀为aba

之后加了一个b之后我们会匹配aba后面的c是否与b匹配,这个时候不匹配,显然最长相同前后缀已经不可能有比aba长了,既然如此,我们是否可以把abacabab看成abab呢?答案是可以的,为什么呢,因为现在最长相同前后缀只能在abacabab中的小于等于3长度的(aba的长度)后缀(bab)中找最大后缀(这里用cnext[7]代替),而abab后缀集合一定包含cnext[7];那么我们在新增字符不匹配的情况下就可以将abacabab看成abab去找其最长相同前后缀,以此类推直到找到最长相同前后缀或者找不到为止。

模拟下剩下的步骤:这个时候abab中的aba最长相同前后缀为a,将b与前缀a后面的b比较,相同,则abab最长相同前后缀为ab。

KMP算法是用一个和模板串相同长度的数组next[]保存每个模板串前缀中的最长相同前后缀的长度(next[n-1]的值是长度为n的前缀子串对应的最长相同前后缀的长度),下面给出求next[]的代码:

 

void makeNext(const char P[],int next[])

{

    int len= strlen(P);

    next[0] = 0;

    for (int q = 1,k = 0; q < len; ++q)

    {

        while(k > 0 && P[q] != P[k])

            k = next[k-1];

        if (P[q] == P[k])

        {

            k++;

        }

        next[q] = k;

    }

}

 

标签:字符,匹配,大串,后缀,长度,算法,KMP,易懂,模板
来源: https://blog.csdn.net/qq_33907218/article/details/78573635

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有