manacher算法求最长回文串

版权声明:原创文章,转载请注明出处。本博新地址www.iaccepted.net https://blog.csdn.net/IAccepted/article/details/48347957

求最长回文串可以使用manacher算法来达到O(n)时间内得出结果,之所以降到O(n)是因为减少了很多重复匹配。

思路如下:

1.把所有字符串都变成奇数个字母的串,方法很简单,就是在所有字母前后加一个特殊字符,比如常用’#’,这样长度为n的串就变成了长度为2*n+1的串,即奇数串,然后在为了在代码中减少边界的判断,分别在首位加另外的特殊字符,一般在首部加个’$’,而尾部自然用字符串的结尾标志’\0’即可,所以总长度为2 *n + 3,另外最后一个字符在新串中的下标为2 *n,所以p数组只需要申请2 *n+1个元素即可。
如:abcd 变成$#a#b#c#d#,最后的’\0’不显示。

2.减少重复查找的关键地方就是如下代码中的

if (i < mx)
{
    p[i] = minx(p[2 * id - i], mx - i);
}

这里分析一下:
当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],见下图。
iaccepted的博客
当 P[j] >= mx - i 的时候,以S[j]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能逐个去匹配了。
iaccepted的博客
对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。

3.另外,转换后的字符串的回文子串的半径(即P[]的值)减1就是原字符串中回文子串的长度(包含的任何一个回文子串都有这个关系)

代码如下:

int maxPalin(const char *str)
{
    unsigned len = strlen(str);
    char *s = new char[2 * len + 3];
    int *p = new int[2 * len + 1];

    s[2 * len + 2] = '\0';
    s[0] = '$';

    for (unsigned i = 0; i < len; ++i)
    {
        s[2 * i + 1] = '#';
        s[2 * i + 2] = str[i];
    }

    s[2 * len + 1] = '#';

    int id = 0;
    int mx = 0;

    int res = -1;

    for (unsigned i = 1; i <= 2 * len; ++i)
    {
        if (i < mx)
        {
            p[i] = minx(p[2 * id - i], mx - i);
        }
        else
        {
            p[i] = 1;
        }

        while (s[i + p[i]] == s[i - p[i]])
        {
            ++p[i];
            if (p[i] > res)res = p[i];
        }

        if (i + p[i] > mx)
        {
            mx = i + p[i];
            id = i;
        }
    }

    delete[] p;
    delete[] s;
    return res - 1;
}

博客地址:www.iaccepted.net

猜你喜欢

转载自blog.csdn.net/IAccepted/article/details/48347957
今日推荐