KMP算法是由D. E. Knuth,J. H. Morris和V. R. Pratt提出来的一种字符串匹配改进算法,和BF算法相比,KMP算法的最大特点是主串指针不回溯,当匹配失败后,由之前比较所得的信息来决定模式串中的哪个字符和主串指针所指字符再比较。因此这个算法涉及到的主要问题就是求匹配失败之后,应该用模式串中哪个字符和主串指针所指字符再比较。
假设主字符串为S0 S1 S2 … Sn,模式串为P0 P1 P2 … Pm,匹配失败时,主串指针所指位置为Si,模式串指针所指位置为Pj。若接下来主串字符Si(i指针不回溯)应与模式串中第k(k < j)个字符继续比较,则模式串中前k-1个字符必须满足下列关系式1),且不可能存在更大的k' > k满足下列关系式1)。
1) P1 P2 … Pk-1 = Si-k+1 Si-k+2 … Si-1
而由之前的比较可以得到的匹配结果是:
2) Pj-k+1 Pj-k+2 … Pj-1 = Si-k+1 Si-k+2 … Si-1
因此,由式1)和2)可以推出:
3) P1 P2 … Pk-1 = Pj-k+1 Pj-k+2 … Pj-1
由式子3),可以很清楚的看到,位置k的确定和主字符串无关,只要在模式串中找到满足式子3)的两个子串(子串越大越好),就可以确定k的位置。
KMP算法如下:
- void get_next(SString T, int next[])
- {
- int j = 0, k = -1;
- next[0] = -1;
- while (j < T.Length - 1) {
- if (k == -1 || T[j] == T[k]) {
- j++;
- k++;
- next[j] = k;
- } else {
- k = next[k];
- }
- }
- }
- int KMP(SString S, SString T)
- {
- int next[MAX_SIZE], i = 0, j = 0;
- get_next(T, next);
- while (i < S.Length && j < T.Length) {
- if (j == -1 || S[i] == T[j]) {
- i++;
- j++;
- } else {
- j = next[j];
- }
- }
- if (j >= T.Length)
- return i - T.Length;
- else
- return -1;
- }
由于KMP算法有效的利用了之前比较所得的信息,主串指针不需要回溯。因此,和BF算法相比提高了模式匹配的效率,特别是在最坏的情况下。KMP算法的时间复杂度为O(n+m)。