KMP算法详解(C++实现)
在数据结构课上老师讲了kmp算法,但当时并没太懂,现在把思路重新理一遍。
1.kmp算法简介
KMP是三位大牛:D.E.Knuth、J.H.Morris和V.R.Pratt同时发现的。 KMP算法其实就是一种改进的字符串匹配算法,关键是利用匹配后失败的信息,尽量减少模式串(W)与主串(T)的匹配次数以达到快速匹配的目的。具体实现就是实现一个next() 函数,函数本身包含了模式串的局部匹配信息。时间复杂度 O(m+n)。 如果考虑最笨的方法,我们可以将T[0]和W[0]进行匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1]跟W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,时间复杂度为O(m*n)。 KMP算法利用已经部分匹配这个有效信息,保持i指针(主串)不回溯,通过修改j指针,让模式串尽量地移动到有效的位置,具体可见下面一个例子。 如果主串为:a b c a b c d h i j k 模式串为:a b c e 当我们匹配到主串的第四个字符a时,可知a和e不相等,因此需要移向下一位,但其实我们并不需要从模式串中的第一位重新开始比较,因为主串中的前三个字符已经没有未匹配的a了,不可能匹配成功。
2.next()函数
因此,最关键的是找到如何移动j指针。我们记当匹配失败时,j要移动的下一个位置为k(即next[j]= k)。记P为模式串。 很显然,存在这样一个性质:最前面的k个位置(对于模式串来说)和j之前的最后k个字符(主串)是一样的。因此得到公式:
P[0 ~ k-1] == P[j-k ~ j-1]
当P[k] == p[j]时
有P[0 ~ k-1] + P[k] == p[j-k ~ j-1] + P[j],即:P[0 ~ k] == P[j-k ~ j],因此可得
next[j+1] == k + 1 == next[j] + 1
当P[k] != p[j]时
我们只能在0~k-1中去寻找最长后缀串了,因此为
k = next[k]
使用C++ 实现next函数为
int* getNext(string p) { int* next = new int[p.length()]; next[0] = -1; //while the first char not match, i++,j++ int j = 0; int k = -1; while (j < (int)p.length() - 1) { if (k == -1 || p[j] == p[k]) { j++; k++; next[j] = k; } else { k = next[k]; } } return next; }
3.完整算法
int KMP(string T,string p) { int i=0; int j=0; int* next=getNext(T); while (i < (int)T.length() && j < (int)p.length()) { if (j == -1 || T[i] == p[j]) { i++; j++; } else { j=next[j]; } } if (j == (int)p.length()) { return i-j; } return -1; }