学习kmp算法

来源:互联网 发布:网络协议栈是什么 编辑:程序博客网 时间:2024/06/13 04:14

kmp算法是字符串匹配的经典算法,以前看过,但是很久不用都忘记了。在leetcode上刷题时遇到字符串匹配的题,借此机会重新学习整理一下。


问题:有两个字符串A与B,长度分别为n和m。判断A是否包含与B相同的子串,如果有,返回子串位置。

最简单的暴力破解算法,时间复杂度为O(mn)。kmp算法的时间复杂度为O(m + n),分为两个步骤:next数组求解(O(m)),以及利用next数组匹配(O(n))。比如表格中的字符串A和字符串B。

i0123456789A串bababababbB串bababb




我们以最简单的方法进行匹配,从起始位置开始判断A串和B串是否匹配。如果不匹配,则将B串向后移动一位,继续匹配,直到在A串中找到相同的子串或到最后一位仍然无法匹配。按照这种方法,(1)在匹配到i=5时,发现A与B不相等。(2)将B串向后移动一位,发现i=1处不等。(3)继续向后移动,这次匹配到i=7。而按照KMP算法,我们可以跳过步骤(2),直接将B串移动到i=2处开始进行匹配。在匹配时,我们虽然不知道A串整体的模式,但我们可以提前对B串做处理,从而掌握A串中已经匹配的部分的模式,略去像步骤(2)这样不必要的情况。想要达到这样的效果,就需要通过遍历B串得到的next数组实现。

i0123456789A串bababababbB串
bababb





bababb


一. next数组的求解

在上面的例子中,按照KMP算法的方式进行的步骤如下表格所示。可以发现,B[2:5],与B[0:3]相同,即B串的子串的前缀与后缀相同。next数组中的next[i]的值为B串的子串B[0:i+1](不包含i+1)的最大相同前后缀的长度。

i0123456789A串bababababbB串bababb






bababb

B串的next数组如下表所示。则next数组的求解过程为遍历B串,对于每一个元素i,求子串B[0:i+1]的相同前后缀的最大长度。前后缀不相等的即为0。

i012345B串bababbnext001231

(1)当i=0时,next[0]=0。

(2)当遍历到i时,已知next[i-1]=j。
若B[j]==B[i],此时next[i]=j+1。
若B[j]!=B[i],此时只能在B[0:j]子串中找到相应的next数组的值,j=next[j-1],如果此时B[j]==B[i],则next[i]=j+1。否则重复该过程。如果j=next[j-1]==0,则说明字串B[0:i+1]没有相同的前后缀,因此next[i]=0。

(3)当i=B.size()时,退出。

需要注意的是由于next数组中的长度从1开始,字符串的索引从0开始。因此上面说到的B[j]可以写成B[j - 1 + 1]更能反映出它的意义,即字串的前缀的末位在后移一位。另外,B[0:i+1]不包含i+1,应为B(0, 1, ... i)。

下面是根据上述步骤写出的代码。

int n = haystack.size(), m = needle.size(), i, j;        if (m <= 0) return 0;        int *next = new int[m];        next[0] = 0;        for (i = 1, j = 0; i < m; i++) {            while (j && needle[j] != needle[i]) j = next[j - 1];            if (needle[j] == needle[i]) j++;            next[i] = j;        }

二. next数组的应用

得到next数组后,我们就可以利用它进行匹配。下面的表格显示出KMP算法利用next数组匹配的过程,共匹配了三次。

(1)匹配到i=5时a!=b,匹配了B[0:5],查找上表发现j=next[i-1]=3。从B[j-1+1],即B[3]处开始继续比较。

(2)匹配到i=7时a!=b,过程与(1)相同,仍从B[3]处开始继续比较。

(3)匹配到i=9时,B串全部匹配,找到与B串相同的字串。

i0123456789A串bababababbB串bababb






bababb






bababb

程序过程与求next数组相似。遍历数组A,比较A[i]与B[j]是否相等。如果相等,指向A串和B串的索引分别后移一位。如果不等,利用next数组跳转,直到找到与A[i]相等的B[j],特别的,如果j==0,说明B[0:j+1]中没有相等的前后缀,此时A串索引向后移动一位(i++)后继续比较。KMP完整代码如下。

int strStr(string haystack, string needle) {        int n = haystack.size(), m = needle.size(), i, j;        if (m <= 0) return 0;        int *next = new int[m];        next[0] = 0;        for (i = 1, j = 0; i < m; i++) {            while (j && needle[j] != needle[i]) j = next[j - 1];            if (needle[j] == needle[i]) j++;            next[i] = j;        }                for (i = 0, j = 0; i < n; i++) {            while (j && haystack[i] != needle[j]) j = next[j - 1];            if (haystack[i] == needle[j]) j++;            if (j == m) return i - m + 1;        }        return -1;}


参考:http://blog.csdn.net/power721/article/details/6132380

http://www.hihocoder.com/






1 0
原创粉丝点击