欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > > 文章正文

LeetCode,(2)

来源: javaer 分享于  点击 34980 次 点评:154
$”

    算法的核心思想就是,可以看到中间“abacaba”是最长回文,当我们已经算到了 “c” 的位置,知道p[9] = 5(如果我没算错的话- -),那当我们算 “p” 这个位置的时候,发现“p”位置的下标减去 “c” 的下标,大于“c”位置(center)的值,也就是在以“c”为中心的最长回文串的外边,那么我们肯定是不知道什么对“p”位置的值有贡献的东西,只能用第一种方法,中心扩充,来计算这个位置的值。

    但是!如果我们要算 “c” 后边的 “b” 的值的时候,我们知道了这个 “b” 是处在 “c” 的回文子串之中的,所以如果它的下标是 i,那么它对应过去的下标 mirror_i 位置上一定也是b,这时候需要判断对应位置上的回文值,因为对应位置一定已经计算过了,所以通过镜像过去位置上的回文值计算当前位置的回文值,能够避免掉很多运算

    如果对应位置的回文长度不如 “c” 的回文长度长,那么当前位置的一定也就是那么长,因为是镜像的!下图就是实例:

    但是如果镜像位置上的回文长度超出了 “c” 辐射的范围,那么当前位置上的就是至少能够达到碰到 “c” 辐射范围的边缘。如下图所示。

    总结来说,就是在从左往右遍历整个字符串的过程中,通过已有信息,跳过不必要的判断,共有三种情况:

    1、要计算的位置在已知最大的回文串覆盖范围外 —— 只能通过中心扩展一点一点算,没有跳过计算;

    2、要计算的位置在已知最大回文串覆盖范围内,且镜像位置上的回文串被最大回文串包含 —— 不需要计算,直接 p[i] = p[mirror_i] 即可,跳过这个位置所有计算;

    3、要计算的位置在已知最大回文串覆盖范围内,但镜像位置上的回文串没有完全被最大回文串包含 —— 可以从最大回文串的边缘开始中心扩充,能跳过一部分计算。

    看懂了算法思想,代码就比较简单了:

// Transform S into T.
// For example, S = "abba", T = "^#a#b#b#a#$".
// ^ and $ signs are sentinels appended to each end to avoid bounds checking
string preProcess(string s)
{
    int n = s.length();
    if (n == 0) return "^$";
    string ret = "^";
    for (int i = 0; i < n; i++)
        ret += "#" + s.substr(i, 1);
    ret += "#$";
    return ret;
}
 
string longestPalindrome(string s)
{
    string T = preProcess(s);
    int n = T.length();
    int *P = new int[n];
    int C = 0, R = 0;                 // center, right
    for (int i = 1; i < n-1; i++)
    {
        int i_mirror = 2 * C - i;     // equals to i' = C - (i-C)
        P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0;
        // Attempt to expand palindrome centered at i
        while (T[i + 1 + P[i]] == T[i - 1 - P[i]])
            P[i]++;
        // If palindrome centered at i expand past R, adjust center based on expanded palindrome.
        if (i + P[i] > R)
        {
            C = i;
            R = i + P[i];
        }
    }
    // Find the maximum element in P.
    int maxLen = 0;
    int centerIndex = 0;
    for (int i = 1; i < n-1; i++)
    {
        if (P[i] > maxLen)
        {
            maxLen = P[i];
            centerIndex = i;
        }
    }
    delete[] P;
    return s.substr((centerIndex - 1 - maxLen) / 2, maxLen);
}

     由于这个算法其实最关心的就是 right 边界,每次就是移动 R, 算法可以保证 finish in 2 * n steps(具体为什么是2*n还没有很明白),所以是 O(N) 的时间富足度。

方法四:后缀树(Suffix Trees)

    并没有看

相关文章

    暂无相关文章

用户点评