Codeforces 1326D1 - Prefix-Suffix Palindrome (Easy version)

题目大意

T组数据,每组给定一个字符串 s

求一个最长的字符串 t ,满足:

  1. t 是一个回文串

  2. 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<<'\n';
        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<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while(T--)
        solve();
    
    return 0;
}
posted @ 2020-03-20 06:18  StelaYuri  阅读(325)  评论(0编辑  收藏  举报