这题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; }