KMP未优化:
#include <iostream> #include <string> using namespace std; /* P 为模式串,下标从 0 开始 */ void GetNext(string P, int next[]) { int p_len = P.size(); int i = 0; // P 的下标 int j = -1; next[0] = -1; while (i < p_len - 1) { if (j == -1 || P[i] == P[j]) { i++; j++; next[i] = j; } else j = next[j]; } } /* 在 S 中找到 P 第一次出现的位置 */ int KMP(string S, string P, int next[]) { GetNext(P, next); int i = 0; // S 的下标 int j = 0; // P 的下标 int s_len = S.size(); int p_len = P.size(); while (i < s_len && j < p_len) { if (j == -1 || S[i] == P[j]) // P 的第一个字符不匹配或 S[i] == P[j] { i++; j++; } else j = next[j]; // 当前字符匹配失败,进行跳转 } if (j == p_len) // 匹配成功 return i - j; return -1; }
KMP优化:
1 void GetNextval(string P, int nextval[]) 2 { 3 int p_len = P.size(); 4 int i = 0; // P 的下标 5 int j = -1; 6 nextval[0] = -1; 7 8 while (i < p_len - 1) 9 { 10 if (j == -1 || P[i] == P[j]) 11 { 12 i++; 13 j++; 14 15 if (P[i] != P[j]) 16 nextval[i] = j; 17 else 18 nextval[i] = nextval[j]; // 既然相同就继续往前找真前缀 19 } 20 else 21 j = nextval[j]; 22 } 23 }
KMP算法(未优化版): next数组表示最长的相同真前后缀的长度,我们不仅可以利用next来解决模式串的匹配问题,也可以用来解决类似字符串重复问题等等,这类问题大家可以在各大OJ找到,这里不作过多表述。
KMP算法(优化版): 根据代码很容易知道(名称也改为了nextval),优化后的next仅仅表示相同真前后缀的长度,但不一定是最长(称其为“最优相同真前后缀”更为恰当)。此时我们利用优化后的next可以在模式串匹配问题中以更快的速度得到我们的答案(相较于未优化版),但是上述所说的字符串重复问题,优化版本则束手无策。
EXKMP:
1 void Get_Next(char *S, int *next) { 2 int lenS = strlen(S + 1), p = 1, pos; 3 next[1] = lenS; // 对于 next[1] 要特殊考虑 4 while (p + 1 <= lenS && S[p] == S[p + 1]) ++ p; 5 next[pos = 2] = p - 1; // next[2] 是为了初始化 6 7 For (i, 3, lenS) { // 注意此时 k + 1 = i 8 int len = next[i - pos + 1]; 9 if (len + i < p + 1) next[i] = len; // 对应上面第一种情况 10 else { 11 int j = max(p - i + 1, 0); // 找到前面对于 子串 最靠后已经匹配的位置 12 while (i + j <= lenS && S[j + 1] == S[i + j]) ++ j; // 第二种需要暴力匹配 13 p = i + (next[pos = i] = j) - 1; // 记得更新 p, pos 14 } 15 } 16 } 17 18 void ExKMP(char *S, char *T, int *next, int *extend) { 19 int lenS = strlen(S + 1), lenT = strlen(T + 1), p = 1, pos; 20 21 while (p <= lenT && S[p] == T[p]) ++ p; 22 p = extend[pos = 1] = p - 1; // 初始化 extend[1] 23 24 For (i, 2, lenS) { 25 int len = next[i - pos + 1]; 26 if (len + i < p + 1) extend[i] = len; 27 else { 28 int j = max(p - i + 1, 0); 29 while (i + j <= lenS && j <= lenT && T[j + 1] == S[i + j]) ++ j; 30 p = i + (extend[pos = i] = j) - 1; 31 } 32 } // 和上面基本一模一样啦 33 }