题目:
经典的KMP算法
分析:
和KMP算法对应的是BF算法,其中BF算法时间复杂度,最坏情况下可以达到O(n*m),而KMP算法的时间复杂度是O(n + m),所以,KMP算法效率高很多。
但是KMP算法不太好理解,其中牵涉到next数组,目标就是让模式串尽可能的往右滑动,减少比较次数,比如
a b a b c
-1 0 0 1 2
比如我们比较ababc时,如果c比较发现错误,前面的abab已经比较成功,那么下次比较,我们只需要从aba的最后一个a开始比较,这样省去了从头开始比较。
算法代码:
[cpp]
view plain
copy
- #include <iostream>
- #include <cstdlib>
- #include <cstdio>
- using namespace std;
- //这是整个kmp中最核心的地方
- int get_next(const char*t, int *next)
- {
- int i = 0;
- int j = -1; //设置j = -1,非常巧妙
- int len = strlen(t);
- memset(next,0, sizeof(int) * len);
- next[0] = -1;
- while(i < len – 1)
- {
- if(j == -1 || t[i] == t[j]) //前面的判断,j == -1, 非常巧妙
- {
- i++;
- j++;
- next[i] = j; //将后面的next数组元素赋值
- }
- else
- j = next[j];
- }
- }
- int kmp(const char *s, const char *t)
- {
- int i = 0;
- int j = 0;
- int next[100];
- get_next(t,next);
- while(i < strlen(s) && j < strlen(t))
- {
- if(j == – 1 || s[i] == t[j]) //如果j为-1,或者模式串和主串相等,两者继续往下比较
- {
- i++;
- j++;
- }
- else
- j = next[j];
- }
- if(j >= (int)strlen(t))
- {
- cout << “found “ << endl;
- return 0;
- }
- cout << “not found” <<endl;
- return 0;
- }
- //暴力法
- int brute_force(const char *s, const char *t)
- {
- int i, j;
- i = 0;
- while(i < strlen(s))
- {
- j = 0;
- while(j < strlen(t))
- {
- if(s[i] == t[j])
- {
- i++;
- j++;
- }
- else
- {
- i = i – j + 1;
- break;
- }
- }
- if(j == (int)strlen(t))
- {
- cout << “found” << endl;
- return 0;
- }
- }
- cout << “not found” << endl;
- return 0;
- }
- int main()
- {
- brute_force(“abcdef”, “abcdef”);
- kmp(“abcdef”, “aaaa”);
- return 0;
- }
总结:
KMP算法非常经典,同时这个算法实现很多地方非常巧妙。
优化思路
KMP算法是可以被进一步优化的。 我们以一个例子来说明。譬如我们给的P字符串是“abcdaabcab”,经过KMP算法,应当得到“
特征向量”如下表所示:
下标i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
p(i) | a | b | c | d | a | a | b | c | a | b |
next[i] | -1 | 0 | 0 | 0 | 0 | 1 | 1 | 2 | 3 | 1 |
但是,如果此时发现p(i) == p(k),那么应当将相应的next[i]的值更改为next[k]的值。经过优化后可以得到下面的表格:
下标i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
p(i) | a | b | c | d | a | a | b | c | a | b |
next[i] | -1 | 0 | 0 | 0 | 0 | 1 | 1 | 2 | 3 | 1 |
优化的next[i] | -1 | 0 | 0 | 0 | -1 | 1 | 0 | 0 | 3 | 0 |
(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。 (2)next[j]= -1 意义:模式串T中下标为j的字符,如果与首字符 相同,且j的前面的1—k个字符与开头的1—k 个字符不等(或者相等但T[k]==T[j])(1≤k<j)。 如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6] (3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个 字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。 即T[0]T[1]T[2]。。。T[k-1]== T[j-k]T[j-k+1]T[j-k+2]…T[j-1] 且T[j] != T[k].(1≤k<j); (4) next[j]=0 意义:除(1)(2)(3)的其他情况。