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;
}
View Code

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 }
View Code

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 }
View Code
02-12 10:27