几个月没写马拉车了,比赛的时候调了半天,代码也写的极丑
题意:
给你字符串s,让你找到一个最长的回文串a+b,字符串a为s的前缀,b为s的后缀
题解:
先对s做一遍马拉车
细分有四种情况:
1.b为空,即最长的回文串就是某前缀,代码中该长度记为le
2.a为空,即最长的回文串就是某后缀,代码中该长度记为ri
3.a>=b,a的前b个字符与b对应为回文串,a串后(a-b)为回文串。代码中old=min(a,b),tmp=max(a,b)-min(a,b)
4.a<b,b的后a个字符与a对应为回文串,b串前(b-a)为回文串。代码中old=min(a,b),tmp=max(a,b)-min(a,b)
注意下细节,边界情况的考虑。为了简单计算,在马拉车过后的Mp[]数组上进行分析
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000050;
char Ma[maxn*2];
int Mp[maxn*2];
int Manacher(char s[],int len)
{
int l = 0;
Ma[l++] = '$';
Ma[l++] = '#';
for(int i = 0; i<len; i++)
{
Ma[l++] = s[i];
Ma[l++] = '#';
}
Ma[l] = 0;
int mx = 0, id = 0;
for(int i = 0; i<l; i++)
{
Mp[i] = mx>i ? min(Mp[2*id-i],mx-i) : 1;
while(i-Mp[i]>=0 && Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
if(i+Mp[i]>mx)
{
mx=i+Mp[i];
id=i;
}
}
return l;
}
char s[maxn];
int p[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%s",s);
int slen = strlen(s);
if(slen==1){
printf("%s\n",s);
continue;
}
int len = Manacher(s,slen),old=0;
int le=0,ri=0,l=0,r=slen-1,n=slen*2;
for(int i = 2; i<len; i++){
if(i-Mp[i]+1<=2){
le=max(le,i-1);
}
if(i+Mp[i]-1>=n){
ri=max(ri,Mp[i]-1);
}
}
while(l<r){
if(s[l]==s[r]){
old++;
l++,r--;
if(l>=r){
printf("%s",s);
break;
}
}
else{
int tmp=0,flag=0;
l=2*(l+1);r=2*(r+1);
for(int i = 2; i<len; i++){
int len=Mp[i]/2+1;
int L=i-Mp[i]+1;
int R=i+Mp[i]-1;
if(L>R) continue;
if(L<=l&&i-l<=r-i&&(i-l+1)>tmp){
tmp=(i-l+1);
flag=0;
}
if(R>=r&&i-l>=r-i&&(r-i+1)>tmp){
tmp=(r-i+1);
flag=1;
}
}
if(tmp+old*2>max(le,ri)){
if(flag){
for(int i=0;i<old;i++) printf("%c",s[i]);
for(int i=slen-tmp-old;i<slen;i++) printf("%c",s[i]);
}
else{
for(int i=0;i<old+tmp;i++) printf("%c",s[i]);
for(int i=slen-old;i<slen;i++) printf("%c",s[i]);
}
}
else{
if(le>ri) for(int i=0;i<le;i++) printf("%c",s[i]);
else for(int i=slen-ri;i<slen;i++) printf("%c",s[i]);
}
break;
}
}
printf("\n");
}
return 0;
}