codility上的问题(27) Helium 2013

这个题目比较难,给定一个字符串S,找到另外一个字符串T,T既是S的前缀,也是S的后缀,并且在中间某个地方也出现一次,并且这三次出现不重合。返回T的最长长度。例如:输入数据是"barbararhubarb" ,输出为1。虽然barb也既是前缀也是后缀,但是在中间没出现过。

输入串长度N, [0..10^6] 只包含26个字母。

要求复杂度:时间空间都是O(N)。

分析: 长度包含0……比较可恶,干脆把长度小于3的都扔回去,因为要至少3次出现不重合。我们计算如下一个函数p, p[x]表示s[x..N - 1]这个子串和s本身的最长公共前缀的长度。我们不定义p[0],所以x > 0。我们试图线性时间计算p。

假设我们已经计算出p[1],p[2],p[i - 1],我们定义 right = max{p[y] + y - 1},即right是前面求得所有前缀的最右边界,left是right取得最大值的y。也就是说s[left..right]和s[0..right - left + 1]是一样的。初始定义left = right = -1。

(1) 如果我们发现,right >=i, 这说明 i被包含在窗口内,于是我们发现s[i..right]和s[i'..right - left + 1]是一样的,其中i' = i - left。而p[i']已经计算过了。

(1a) 如果p[i'] < right - i + 1, 这说明这个字符串完全被窗口限定住了,我们立刻得到p[i] = p[i']。

  (1b) 如果p[i'] >= right - i + 1,这时所得到的前缀可能更长,我们已经至少有p[i] >= p[i']了。我们继续比较(right + 1)和(i' + p[i']) …… 直到不match,然后更新right和left 。

(2) 如果我们发现right < i, 那么只好沿着i暴力比较前缀,可能的话,更新left和right。 这个算法叫做Z方法,从一本书上看到的……

时间复杂度是O(N)的,因为right几乎每次都会变,如果不变的话,最多浪费一次字符串比较(第一次比较就不相等)。

如果我们能计算出这个p,那么我们枚举最后所求的长度。len = n / 3逆向枚举。

如果后缀满足条件 必须有 p[n - len] == len

如果中间还得出现一次并且不重叠 必须有 max{p[len],p[len + 1],...p[n - len * 2]} >= len,如果len么次增加1,这个集合每次左边多一个候选值,右边多两个候选值,动态更新就可以了。这个应该算一道比较难的面试题了。

代码:

// you can also use includes, for example:
// #include <algorithm>
#include <vector>

int solution(string &S) {
    // write your code here...
    int n = S.length(),left,right,i,j,m,len;
    if (n < 3) {
        return 0;
    }

    vector<int> p;
    p.resize(n, 0);
    left = right = -1;
    for (i = 1; i < n; ++i) {
       
        if (right < i) {
            for (j = 0; (i + j < n) && (S[j] == S[j + i]); ++j)
            ;
            if (j) {
            	p[i] = j;
            	left = i;
            	right = i + j - 1;
            }
        }
        else if (right - i < p[i - left]) {  // right - i + 1 <= p[i - left]
            for (j = right + 1; (j < n) && (S[j] == S[j - i]); ++j)
            ;
            p[i] = j - i;
            left = i;
            right = j - 1;
        }
        else {
            p[i] = p[i - left];
        }
       
    }
    len = n / 3;
    j = n - len * 2;
    for (m = 0, i = len; i <= j; ++i) {
        m = max(m, p[i]);
        if ((m >= len) && (p[n - len] == len)) {
            return len;
        }
    }
    for (--len; len > 0; --len) {
        m = max(m, p[len]);
        m = max(m, p[++j]);
        m = max(m, p[++j]);
        if ((m >= len) && (p[n - len] == len)) {
            break;
        }
    }
    return len;
    
}

 

你可能感兴趣的:(算法,codility)