hihocoder:1032 : 最长回文子串

一、问题描述

现给定一个已知的字符串str[],现在想要在O(n)的时间复杂度之内求出一个最长的回文子字符串(正着和倒着顺序读一致)。

Manacher最早发现了可以用O(n)的时间复杂度来解决该问题,所以这种方法称之为Manacher算法。

二、符号说明

P[]存放的是回文子串的半径

三、Manacher算法主要思想

Manacher算法精髓就是对称的思想,

假设i是我们将要计算回文串半径的字符,就是下一步我们要计算P[i]。
我们已知:
1,以id为中心,P[id]为半径的范围正好可以覆盖i;   if (id+P[id]>i)
2,  以id为对称中心,i的对称点是j,P[j]我们正好也知道。
可以得到:
P[i]>=min( P[id]-(id-j),  P[j] );
原因:如果j的半径较小,那么i的半径必须等于P[j],(如果i的半径比j大,又因为i和j关于id对称,两者就矛盾了,所以i的半径在这种情况下只能等于j的半径); 如下图由对称性1和2相等,2和3相等,3和4相等——》p[j]>p[j]  矛盾! 所以i的半径必须等于P[j]!

如果j的半径较大,那么i的半径只能等于 P[id]-(id-j) (假设i的半径大于P[id]-(id-j),由于对称性,则j的半径要大于P[j],这也于已知矛盾。)如下图:如下图由对称性1和2相等,2和3相等,3和4相等——》p[id]>p[id] 矛盾! 所以i的半径必须等于 P[id]-(id-j)


    除此之外,就是 P[id]为半径 的范围没有覆盖i;  
     这种情况就只能i以自己为中心计算半径了。


四  代码:
和上面图像有些不同,代码里的对称中心是j,所以以j为中心i的对称点就是2 * j - i,
for (int i = 1; i < len; i++)
    {
        if (j+P[j]>i)  //这种情况,j才能作为i的对称中心来简化计算,算法的精髓在P[i] >=min(P[2 * j - i],j+P[j]-i);
        {
            P[i] = min(P[2 * j - i],j+P[j]-i);
        }
        else        //这种情况,p[i]要重新计算,
        {
            P[i] = 1;
        }
        while (newstr[i + P[i]] == newstr[i - P[i]] && i + P[i] <= len)//以i为对称中心,看i两边的元素是否相等,更新p[i]
        {
            P[i]++;
        }
        if ((j + P[j]) < i + P[i])
            j = i;
        if (P[i]>maxlength)//maxlength用来统计最长的回文串长度
            maxlength = P[i];
    }
    return;
}  









猜你喜欢

转载自blog.csdn.net/whatwho_518/article/details/51583243