练习:最长回文子串(Manacher算法)

【例题】

点击这里

【思路】

最长回文子串是个非常经典的问题,Manacher算法是解决它的O(n)优秀算法。

该算法提出在字符串相邻字符间插入字符,从而在中心拓展时无需考虑串长度的奇偶性(显然,对于任意长度为n的串,有n-1个间隔,故而补全串长度为2n-1,总为奇数)。

举个例子:原串str为abababa,长度为7。则补全串s为$#a#b#a#b#a#b#a#*,长度为17。其中#、$和*不属于原串字符集合,#为插入字符,$和*用来标记补全串边界,这方便了中心拓展比较,同时避免索引溢出。

设数组r,r[i]表示补全串s中,以字符s[i]为中心的回文串半径长度(半径含中心字符s[i])。


一旦得出数组r,则只需遍历r并将ans置为最大的r[i]值,输出ans-1即为原串中回文串的最大长度。

可以证明,经过上面的补全后,对于任意i,补全串中以s[i]为中心的回文串去掉所有#后,长度为r[i]-1,即对应的原串中回文子串的长度。

如何求出数组r呢?显然在求r[i]时,要充分利用r[1]、r[2]……r[i-1]这些已经求出的信息。先在这里写出数学表达。


由集合Q确定r[i]的最小值后,还需尝试性地增大r[i]向外围扩展比较,直到遇见s[i-r[i]]≠s[i+r[i]],则已找到以s[i]为中心的最长回文串。

上面的数学表述虽然严谨,但不够具象,下面对该数学形式作通俗阐述。

事实上,j+r[j]反映了1,2……i-1中已经找到的所有回文串的最大覆盖范围。如果j+r[j] j+r[j]-i,则以s[i]为中心的回文串可一直拓展至覆盖边缘。覆盖范围外的部分,则需要我们尝试增大r[i],继续老老实实地比较。

更形象的表述,则可见下图(绿色为重叠部分,蓝色为s[j]中心串,黄色为s[j*2-i]中心串突出部分):



【代码】

#include 
#include 
#define maxSize 1000010
#define min(x,y) (xi) r[i]=min(r[j]+j-i, r[2*j-i]); else r[i]=1;
        while (s[i-r[i]]==s[i+r[i]]) r[i]++;
        if (i+r[i]>j+r[j]) j=i;
    }

    long int ans=0;
    for (i=1;i<=len*2;i++) if (r[i]>ans) ans=r[i];
    return ans-1;
}

int main()
{
    char str[maxSize];
    int n,i;
    scanf("%d",&n);
    for (i=0;i

你可能感兴趣的:(最长回文子串)