L2-008 最长对称子串 [Manacher]

这题O(n^2)的方法能A,但是我们还有Manacher算法可以用。之前学这个还懵逼了好久,太菜了。

这里简单写一写

Manacher用一种巧妙的方法把奇偶数长度的回文串都考虑进去,那就是字符填充。aba填充为#a#b#a#,abba填充为#a#b#b#a#。为了防越界,一般还在字符串前加一个符号,如¥#a#b#a#。

接下来有一个数组p[i]用来记录以字符s[i]为中心的最长回文串半径,p[i]-1正好是原字符串中回文串的总长度。例子证明就不写了。

为了得到p[i],我们需要两个辅助变量,id记录最大回文子串的中心位置,max记录最大的p[id]+id,也就是最大回文子串的边界。接下来就是算法的重点了。尝试直接用文字描述

假设当前我们在求p[i],如果在j=2*id-i(s[j]与s[i]关于id位置对称,j<i)处的 p[j]<max-i,也就是j处的回文串包含在id处的回文串内,那么因为ij对称,所以必有p[i]=p[j]。

那如果p[j]>max-i,即j处的回文串左边超出了id的回文串范围,那么这时候在id回文串内的部分至少还是相同的,max后面的需要继续匹配了

如果max<=i,即i不在当前最大回文串范围内,那只能然p[i]=1,然后再去匹配了,详见代码~

#include <iostream>
#include <string.h>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <math.h>
#include <queue>
#include <vector>
#include <functional>
#define maxn 2005
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
string ts;
char s[maxn];
int a[maxn];
int main()
{
    getline(cin,ts);
    int l=ts.length();
    int p=0;
    s[p++]='@';
    s[p++]='#';
    for(int i=0;i<l;i++)
    {
        s[p++]=ts[i];
        s[p++]='#';
    }
    s[p]='\0';
    int id=0,maxx=0,ans=0;
    for(int i=1;i<p;i++)
    {
        if(maxx>i)
            a[i]=min(maxx-id,a[id*2-1]);
        else
            a[i]=1;
        while(s[i+a[i]]==s[i-a[i]])
            a[i]++;
        if(a[i]+i>maxx)
        {
            id=i;
            maxx=a[i]+i;
        }
        ans=max(ans,a[i]);
    }
    cout<<ans-1<<endl;
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/FTA-Macro/p/10526114.html