【字符串】KnuthMorrisPratt算法

以下模板代码的字符串下标均从1开始。

当遇到性能问题时很可能是vector导致的,换成普通数组也可以。

字符串 (s) 的前缀函数 (pi[i]) 表示长度为 (i) 的前缀中最长的公共前后缀的长度。
kmp 找出模式字符串 (t) 在文本字符串 (s) 中出现的所有的位置。

namespace KnuthMorrisPrattAlgorithm {

    vi getPi(char *s) {
        int slen = strlen(s + 1);
        vi pi(slen + 1);
        for(int i = 2, j = 0; i <= slen; ++i) {
            for(; j && s[i] != s[j + 1]; j = pi[j]);
            j += (s[i] == s[j + 1]);
            pi[i] = j;
        }
        return pi;
    }

    vi kmp(char *s, char *t) {
        int slen = strlen(s + 1), tlen = strlen(t + 1);
        vi pi = getPi(t), res;
        for(int i = 1, j = 0; i <= slen; ++i) {
            for(; j && s[i] != t[j + 1]; j = pi[j]);
            j += (s[i] == t[j + 1]);
            if(j == tlen)
                res.pb(i - tlen + 1);
        }
        return res;
    }

}

字符串 (s) 的z函数 (z[i]) 表示以 (i) 位置为左端点的后缀中,与字符串 (s) 的最长的公共前缀的长度。
exkmp:找出从(s[i])开始的,和模式串 (t) 的最长公共前缀。

namespace ExtendKnuthMorrisPrattAlgorithm {

    vi getZ(char *s, int n) {
        vi z(n + 1);
        z[1] = n;
        for(int i = 2, l = 0, r = 0; i <= n; ++i) {
            if(i <= r)
                z[i] = min(z[i - l + 1], r - i + 1);
            for(; i + z[i] <= n && s[i + z[i]] == s[z[i] + 1]; ++z[i]);
            if(i + z[i] - 1 > r)
                l = i, r = i + z[i] - 1;
        }
        return z;
    }

    vi exkmp(char *s, char *t) {
        int n = strlen(s + 1), m = strlen(t + 1);
        vi z = getZ(t, m), res(n + 1);
        for(int i = 1, l = 0, r = 0; i <= n; ++i) {
            if(i <= r)
                res[i] = min(z[i - l + 1], r - i + 1);
            for(; i + res[i] <= n && s[i + res[i]] == t[res[i] + 1]; ++res[i]);
            if(i + res[i] - 1 > r)
                l = i, r = i + res[i] - 1;
        }
        return res;
    }

}

统计既是字符串 (s) 的前缀又是字符串 (s) 的后缀的所有字符串的长度及其出现次数:易知 (z[i]=(n+1-i)) 表示后缀字符串 (s[i,n]) 与字符串 (s) 的最长公共前缀的长度等于后缀字符串 (s[i,n]) 的长度,所以后缀字符串 (s[i,n]) 满足既是字符串 (s) 的前缀又是字符串 (s) 的后缀,且其长度为 (z[i]) 。那么它的出现次数可以直接用dp求出来(长度为 (z[i]+1) 的字符串出现,则长度为 (z[i]) 的字符串也出现)。

上面这些函数存在越界访问的现象,在对字符串使用时由于字符串末尾一定是特殊字符所以没事,但是对整数数组进行匹配时要小心。

原文地址:https://www.cnblogs.com/purinliang/p/14067447.html