数据结构- KMP
数据结构 - KMP
引言 & 介绍
- 由于李总说过串这一章只讲一个KMP, 所以我这里也就只说一个KMP算法了
- KMP算法, 说得简单点就是关键字搜索
一般方法
- 一般的关键字搜索的算法为:
1 int Search(string a, string b) { 2 int lena = a.length(); 3 int lenb = b.length(); 4 int i = 0, j = 0; 5 6 while (i < lena && j < lenb) { 7 if (a[i] == b[j]) { 8 i++; j++; 9 } else { 10 i = i - j + 1; 11 j = 0; 12 } 13 } 14 return j == lenb ? i - j + 1 : -1; 15 }
- 这个方法的时间复杂度为O(lena * lenb), 可以说是相当高了, 我们可能觉得无所谓, 但是大牛们就会想一种新的算法来优化, 这就是KMP算法, KMP算法的时间复杂度为O(lena + lenb)
KMP算法
KMP函数
- 如果主串a为“abcdabceabcdabcd”, 模式串b为“abcdabcd”, 一般方法在j = 7(从0开始)时就失配了, 但是, 一般方法就会执行很多无意义的部分, 现在, 我们把代码做一点修改:
int KMP(string a, string b) { int lena = a.length(), lenb = b.length(); int i = 0, j = 0; int *next = GetNext(b); while (i < lena && j < lenb) { if (j == -1 || a[i] == b[j]) { i++; j++; } else { j = next[j]; } } return j == lenb ? i - j + 1 : -1; }
求next数组
- 这个代码, 当然是不完整的, 所以这个先看下面这个求next数组的代码:
1 int* GetNext(string b) { 2 int j = 0, k = -1; 3 int len = b.length(); 4 int *next = new int[len + 1]; 5 next[0] = -1; 6 7 while (j < len) { 8 if (k == -1 || b[j] == b[k]) { 9 j++; 10 k++; 11 next[j] = k; 12 } else { 13 k = next[k]; 14 } 15 } 16 return next; 17 }
分析next数组
- 分析next数组前, 我们再说说主串a为“abcdabceabcdabcd”, 模式串b为“abcdabcd”的情况
- 当j = 7的时候a[7] 和 b[7]不匹配, 但是a[0 - 6]和b[0 - 6]是匹配的, 如果b[0 - 6]中的前x个字符和后x个字符一模一样, 那么, 我们就无需把j置零, 只需要把j置为x, 直接比较b[x]和a[7]就可以了
- 而事实上, b[0 - 2]和b[4 - 6]是一样的, 这个x为3, 也可以直接看出, a[4 - 6]和b[0 - 2]是匹配的, 所以我们直接看a[7]和ab[3], 当然啦, 这里a[7] = e, b[3] = d也不匹配, 所以再执行这个步骤·······
- 上面的x就是next[j], 在GetNext函数里面则是next[k]
- 现在应该知道next数组的作用了吧, next[j]表示, 在a[i]与b[j]不匹配时, b的前next[j]个字符, 和后next[j]个字符完全相同
- 知道了next数组的含义之后, 再看上面的函数应该就比较好理解了
GetNext的优化
- 现在考虑a = “aaabaaabaaab”, b = “aaaa”的情况
- next数组为{-1, 0, 1, 2, 3}, 这就意味着, j = 3时失配时, j会从3减到-1, 而不是直接从3跳到-1, 如此完美的算法在这一步出了这种情况岂不可惜, 所以, 这个算法还有最后一步优化
1 int* GetNext(string b) { 2 int j = 0, k = -1; 3 int len = b.length(); 4 int *next = new int[len + 1]; 5 next[0] = -1; 6 7 while (j < len) { 8 if (k == -1 || b[j] == b[k]) { 9 if (b[++j] == b[++k]) { 10 next[j] = next[k]; 11 } else { 12 next[j] = k; 13 } 14 } else { 15 k = next[k]; 16 } 17 } 18 return next; 19 }
- 将GetNext略作修改就好了
代码
- 下面给出完整的KMP算法的代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int* GetNext(string b) { 5 int j = 0, k = -1; 6 int len = b.length(); 7 int *next = new int[len + 1]; 8 next[0] = -1; 9 10 while (j < len) { 11 if (k == -1 || b[j] == b[k]) { 12 if (b[++j] == b[++k]) { 13 next[j] = next[k]; 14 } else { 15 next[j] = k; 16 } 17 } else { 18 k = next[k]; 19 } 20 } 21 return next; 22 } 23 24 int KMP(string a, string b) { 25 int lena = a.length(), lenb = b.length(); 26 int i = 0, j = 0; 27 int *next = GetNext(b); 28 29 while (i < lena && j < lenb) { 30 if (j == -1 || a[i] == b[j]) { 31 i++; j++; 32 } else { 33 j = next[j]; 34 } 35 } 36 return j == lenb ? i - j + 1 : -1; 37 } 38 39 int main () { 40 string a, b; 41 while (cin >> a >> b) { 42 cout << KMP(a, b) << endl; 43 } 44 return 0; 45 }