题目大意
T组数据,每组给定一个字符串 s
求一个最长的字符串 t ,满足:
-
t 是一个回文串
-
t = a+b ,a是字符串s的前缀,b是字符串s的后缀,'+' 为拼接两字符串,ab可能为空串
数据范围
数据组数不超过 1000
字符串的总共长度不超过 5000
解题思路
可直接使用Hard版本算法
或本题直接进行下文暴力法
因为a和b分别是字符串s的前缀和后缀
因为对于任意的字符串T,设R(T)为T的倒置
则 T+回文串+R(T) 仍然是一个回文串
所以可以直接双指针在s里找出最长的 T和R(T)
while(L<R&&s[L]==s[R])
L++,R--;
如果最长的 T 和 R(T) 相拼接恰好等于字符串s(或者中间多一个字符)
那么说明整个 s 就是个回文串,直接输出
否则,考虑中间的子串
根据题意,需要在中间部分寻找最长的回文串
且这个回文串要满足要么与 T 相邻,要么与 R(T) 相邻
因为数据范围很小,所以可以直接暴力
假设中间的子串在s中的位置下标为 [L,R]
左右两侧寻找两次
以左侧为例,寻找的方式如下
- 对于长度为单数的回文串,枚举中间字符的下标 i ,往左右两边 j=i+1,k=i-1 双指针检查
- 对于长度为双数的回文串,枚举中间位置左侧字符的下标 i ,往左右两边 j=i+1,k=i 双指针检查
对于范围,需要注意的是
如果[L,R]内字符个数为奇数,说明以第(L+R)/2个字符为中心的回文串不需要查找(否则整个中间子串就是个回文串),同样以其右侧为中心的回文串也不需要查找
如果[L,R]内字符个数为偶数,只需要查找以第(L+R)/2个字符为中心的回文串即可,不需要查找以(L+R)/2右侧为中心的回文串
int LeftMaxLen=1;
for(i=L;i<=(L+R)/2;i++)
{
if(i==(L+R)/2&&(R-L+1)%2!=0)//i在中心且为奇数直接退出
break;
for(j=i+1,k=i-1;k>=L&&j<=R;j++,k--)
if(s[j]!=s[k])
break;
if(k<L||j>R)
if(LeftMaxLen<(i-L)*2+1)
LeftMaxLen=(i-L)*2+1;
if(i==(L+R)/2)//只要i在中心就退出
break;
for(j=i+1,k=i;k>=L&&j<=R;j++,k--)
if(s[j]!=s[k])
break;
if(k<L||j>R)
if(LeftMaxLen<(i-L+1)*2)
LeftMaxLen=(i-L+1)*2;
}
LeftMaxLen
变量储存左侧回文串的最大长度
因为单个字符也算回文串,故初始值为 1
右边的处理也一样
但是范围处理稍有不同
因为(L+R)/2为向下取整
如果[L,R]内字符个数为奇数,说明以第(L+R)/2个字符为中心的回文串和以其左侧为中心的回文串都不需要查找
如果[L,R]内字符个数为偶数,根据向下取整,只需要判断以第(L+R)/2+1个字符为中心的回文串即可,不需要判断以其左侧为中心的回文串
所以循环条件直接写成i>(L+R)/2
,
int RightMaxLen=1;
for(i=R;i>(L+R)/2;i--)
{
for(j=i+1,k=i-1;j<=R&&k>=L;j++,k--)
if(s[j]!=s[k])
break;
if(j>R||k<L)
if(RightMaxLen<(R-i)*2+1)
RightMaxLen=(R-i)*2+1;
if(i==(L+R)/2+1&&(R-L+1)%2==0)//个数为偶数且i在中心右侧位置,退出
break;
for(j=i,k=i-1;j<=R&&k>=L;j++,k--)
if(s[j]!=s[k])
break;
if(j>R||k<L)
if(RightMaxLen<(R-i+1)*2)
RightMaxLen=(R-i+1)*2;
}
最后,输出左边+中间+右边即可
完整程序
(31ms / 2000ms)
#include<bits/stdc++.h>
using namespace std;
string s;
void solve()
{
cin>>s;
int i,j,k,L=0,R=s.size()-1;
while(L<R&&s[L]==s[R])
L++,R--;
if(L>=R)
{
cout<<s<<'
';
return;
}
int LeftMaxLen=1;
for(i=L;i<=(L+R)/2;i++)
{
if(i==(L+R)/2&&(R-L+1)%2!=0)
break;
for(j=i+1,k=i-1;k>=L&&j<=R;j++,k--)
if(s[j]!=s[k])
break;
if(k<L||j>R)
if(LeftMaxLen<(i-L)*2+1)
LeftMaxLen=(i-L)*2+1;
if(i==(L+R)/2)
break;
for(j=i+1,k=i;k>=L&&j<=R;j++,k--)
if(s[j]!=s[k])
break;
if(k<L||j>R)
if(LeftMaxLen<(i-L+1)*2)
LeftMaxLen=(i-L+1)*2;
}
int RightMaxLen=1;
for(i=R;i>(L+R)/2;i--)
{
for(j=i+1,k=i-1;j<=R&&k>=L;j++,k--)
if(s[j]!=s[k])
break;
if(j>R||k<L)
if(RightMaxLen<(R-i)*2+1)
RightMaxLen=(R-i)*2+1;
if(i==(L+R)/2+1&&(R-L+1)%2==0)
break;
for(j=i,k=i-1;j<=R&&k>=L;j++,k--)
if(s[j]!=s[k])
break;
if(j>R||k<L)
if(RightMaxLen<(R-i+1)*2)
RightMaxLen=(R-i+1)*2;
}
for(i=0;i<L;i++)
cout<<s[i];
if(LeftMaxLen>=RightMaxLen)
for(i=L,j=0;j<LeftMaxLen;i++,j++)
cout<<s[i];
else
for(i=R-RightMaxLen+1;i<=R;i++)
cout<<s[i];
for(i=R+1;i<s.size();i++)
cout<<s[i];
cout<<'
';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}