链接
http://codeforces.com/contest/1326/problem/D2
题意
a a a 是 s s s 的前缀, b b b 是 s s s 的后缀
使 a + b a+b a+b 是可以找到的最大的回文串
a a a 或 b b b 可以是空串
思路
找出最大长度 k k k 使 s [ 0 , k − 1 ] + s [ l e n ( s ) − k , l e n ( s ) − 1 ] s[0,k-1]+s[len(s)-k,len(s)-1] s[0,k−1]+s[len(s)−k,len(s)−1] 是回文串
再用 Manacher 算法求出 s [ k , l e n ( s ) − k − 1 ] s[k,len(s)-k-1] s[k,len(s)−k−1] 中以 s [ k ] s[k] s[k] 为右端的的最大回文串和以 s [ l e n ( s ) − k − 1 ] s[len(s)-k-1] s[len(s)−k−1]为左端的最大回文串(计算出 l e n [ i ] len[i] len[i] 后,判断以 i i i 为中心的最大回文串的左端和右端即可)
把较大的那个回文串加在开始找到的长度为 2 ∗ k 2*k 2∗k 的回文串中间即是最大答案
注意:当一开始找长度 k k k 时 k − 1 ≥ l e n ( s ) − k k-1\ge len(s)-k k−1≥len(s)−k 时, s s s 本身就是一个回文串
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char s[N],str[N*2];
int len[N*2];
pair<int,int> Manacher(int l,int r) {
str[0]='$';
int k=0;
for(int i=l;i<=r;i++) str[++k]='#',str[++k]=s[i];
str[++k]='#';
len[0]=0;
int id=0,mx=0;
int mxl=0,mxr=0;
for(int i=1;i<=k;i++) {
len[i]=i<mx?min(mx-i,len[2*id-i]):1;
while(i-len[i]>=0&&i+len[i]<=k&&str[i-len[i]]==str[i+len[i]]) ++len[i];
if(len[i]+i>mx)mx=len[i]+i,id=i;
if(len[i]==i) {
if(len[i]-1>mxl) mxl=len[i]-1;
}
else if(len[i]==k-i+1) {
if(len[i]-1>mxr) mxr=len[i]-1;
}
}
if(mxl>mxr) return make_pair(0,mxl);
else return make_pair(1,mxr);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;cin>>T;
while(T--) {
cin>>s;
int l=0,r=strlen(s)-1;
while(l<r&&s[l]==s[r]) l++,r--;
if(l>=r){cout<<s<<endl;continue;}
pair<int,int> res=Manacher(l,r);
for(int i=0;i<l;i++) cout<<s[i];
if(res.first==0) for(int i=l;i<=l+res.second-1;i++) cout<<s[i];
else if(res.first==1) for(int i=r-res.second+1;i<=r;i++) cout<<s[i];
for(int i=l-1;i>=0;i--) cout<<s[i];
cout<<endl;
}
return 0;
}