Codeforces 1326D2 - Prefix-Suffix Palindrome (Hard version)

题目大意

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

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

  1. t 是一个回文串
  2. t = a+b ,a是字符串s的前缀,b是字符串s的后缀,'+' 为拼接两字符串,ab可能为空串

数据范围

数据组数不超过 1e5

字符串的总共长度不超过 1e6


解题思路

(标准做法应该是哈希)

因为对于任意的字符串T,设R(T)为T的倒置

T+回文串+R(T) 仍然是一个回文串

可以直接双指针在s里找出最长的 T和R(T)

while(L<R&&s[L]==s[R])
    L++,R--;

(上面这步O(n)不能省略)


但是因为数据范围内的字符数量很大,所以不能继续采用Easy版的O(n^2)暴力法

很容易想到,对于中间剩下的子串,要想求出与两边相邻的最长回文串

就可以用Manacher算法来 O(n) 处理与计算

在初始化函数里传入两个变量 l 和 r ,表示只要处理 [ l , r ] 区间内的字符即可

void initStr(int l,int r){
	int k=0;
	str[k++]='@';
	for(int i=l;i<=r;i++){
		str[k++]='#';
		str[k++]=s[i];
	}
	str[k++]='#';
	len=k;
	str[k]='\0';
}

在Manacher处理过程中,计算出一个Len值就可以判断一次是不是与左或者与右相邻

void manacher(){
	int mx=0,id=0;
	for(int i=1;i<len;i++){
		if(mx>i)
			Len[i]=min(mx-i,Len[2*id-i]);
		else
			Len[i]=1;
		while(str[i+Len[i]]==str[i-Len[i]])
			Len[i]++;
		if(Len[i]+i>mx){
			mx=Len[i]+i;
			id=i;
		}
		
		if(i==Len[i])//说明此时这个回文串左相邻
			if(TP<Len[i])
				TP=Len[i]-1,GR=1;
		if(i+Len[i]==len)//说明此时这个回文串右相邻
			if(TP<Len[i])
				TP=Len[i]-1,GR=2;
	}
}

上面的代码中,TP表示最长回文串长度,GR为1表示左相邻,2表示右相邻


最后输出左+中+右即可





完整代码

(77ms / 2000ms)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+50;

string s;
char str[MAXN*2];
int Len[MAXN*2],len,TP,GR;

void initStr(int l,int r){
	int k=0;
	str[k++]='@';
	for(int i=l;i<=r;i++){
		str[k++]='#';
		str[k++]=s[i];
	}
	str[k++]='#';
	len=k;
	str[k]='\0';
}

void manacher(){
	int mx=0,id=0;
	for(int i=1;i<len;i++){
		if(mx>i)
			Len[i]=min(mx-i,Len[2*id-i]);
		else
			Len[i]=1;
		while(str[i+Len[i]]==str[i-Len[i]])
			Len[i]++;
		if(Len[i]+i>mx){
			mx=Len[i]+i;
			id=i;
		}
        
		if(i==Len[i])
			if(TP<Len[i])
				TP=Len[i]-1,GR=1;
		if(i+Len[i]==len)
			if(TP<Len[i])
				TP=Len[i]-1,GR=2;
	}
}

void solve()
{
	cin>>s;
	int LEN=s.size();
	
	int L=0,R=LEN-1;
	while(L<R&&s[L]==s[R])
		L++,R--;
	
	if(L>=R)
	{
		cout<<s<<'\n';
		return;
	}
	
	initStr(L,R);
	TP=0;
	manacher();
	for(int i=0;i<L;i++)
		cout<<s[i];
	if(GR==1)
	{
		for(int i=L,j=0;j<TP;i++,j++)
			cout<<s[i];
	}
	else
	{
		for(int i=R-TP+1;i<=R;i++)
			cout<<s[i];
	}
	for(int i=R+1;i<LEN;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 05:37  StelaYuri  阅读(412)  评论(0编辑  收藏  举报