KMP算法

整体来看,KMP算法,在s中寻找p(p,pattern,s可能的子串)采用的思想是:
1.预处理p,生成next数组;
2.遍历中s的index不回溯,不匹配时下一个字符匹配p的next[j]字符,问题的复杂度由O(mn)下降到O(m+n)。;
3.next数组有自动状态机的思想在里面;
4.next数组其实是前缀数组的最前面和最后面共同的字符数。next数组有从-1开始的,有从0开始的。算法导论从0开始,但数组下标从1开始。我们采取从-1开始的,这样j=next[j]好写。

以"ABCDABD"为例,

- "A"的前缀和后缀都为空集,共有元素的长度为0;
- "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
- "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
- "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
- "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
- "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
- "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

所以这里他的next数组是[-1, 0, 0, 0, 0, 1, 2]。(next[i]表示长度为i的字符串前缀的上述共有前后缀)

5.KMP的比较算法和Next数组的生成算法很像的,相差之处一句next[j]=k;即求next数组的赋值,j的初始值也不一样。

参考:

http://www-igm.univ-mlv.fr/~lecroq/string/node8.html

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

 在下面的实现中,因为s.length()是unsigned int,所以先转化成int,否则和-1比较会有问题。因为next数组第一个是-1,所以先设置好,然后j也设为-1。

void get_next(const string &t, vector<int> &next) {
    next.resize(t.size());
    int i = 0;
    int j = -1;
    next[0] = -1;
    while(i < t.size() - 1) {
        if(j == -1 || t[i] == t[j]) {
            next[++i] = ++j;
        }
        else {
            j = next[j];
        }
    }
}

  

int KMP(const string &t, const string &s) {
    vector<int> next;
    get_next(t, next);
    int i = 0;
    int j = 0;
    int slen = s.length();
    int tlen = t.length();
    while(i < slen && j < tlen) {
        if (j == -1 || t[j] == s[i]) {
            i++;
            j++;
        }
        else {
            j = next[j];
        }
    }
    if(j == tlen)
        return i - j;
    else
        return -1;
}

  

原文地址:https://www.cnblogs.com/lautsie/p/3228877.html