题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5340
解题思路:首先用manacher处理每个字符,接下来就是要枚举了。
首先是我想到的dp,dp[i][j]表示第i个字符结尾的第j个回文串是否存在。
dp[i+k-1][j] = 1 if(dp[i-k+1][j-1] == 1),k当前表示回文串的半径长度
最后只需要判断dp[len][3]即可。
但很可惜超时了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 20005;
int p[maxn<<1],dp[maxn<<1][4];
char str[maxn],c[maxn];
void manacher(int len)
{
int id = 0;
p[0] = 1;
for(int i = 1; i <= 2 * len; i++)
{
if(id + p[id] > i) p[i] = min(p[2*id-i],id + p[id] - i);
else p[i] = 1;
while(i-p[i]>=0 && i+p[i]<=2*len && c[i-p[i]]==c[i+p[i]]) p[i]++;
if(i+p[i] > id+p[id])
id = i;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%s",str+1);
int len = strlen(str+1);
c[0] = '#';
for(int i = 1; i <= len; i++)
{
c[2*i-1] = str[i];
c[2*i] = '#';
}
manacher(len);
dp[0][0] = 1;
for(int i = 1; i < 2 * len; i++)
for(int j = 1 + (i % 2 == 0); j <= p[i]; j++)
for(int k = 1; k <= 3; k++)
{
if(dp[i - j + 1][k-1])
dp[i + j - 1][k] = 1;
}
if(dp[2*len][3])
printf("Yes\n");
else printf("No\n");
}
return 0;
}
其实这里可以枚举第一个串和第三个串的末尾和起始位置,这样就只需要判断中间位置的串是否是回文即可。
#include <cstdio>
#include <algorithm>
#define MAXN 20010
using namespace std;
int n;
char d[MAXN];///原始字符串
char st[MAXN*2];///经过manacher处理之后的字符串
int p[MAXN*2];///保存回文串半径,ps每个回文串长度一定为奇数
int ll[MAXN*2];///第一个回文串可能的所有半径
int rr[MAXN*2];///第三个回文串可能的所有半径
void manacher(){
int i;
st[0]='$';
st[1]='#';
for(i=1;d[i]!='\0';++i){
st[i*2]=d[i];
st[i*2+1]='#';
}
st[i*2]='\0';
n=i*2-1;
int MaxId=0,id;
for(int i=1;i<=n;i++)
{
if(MaxId>i)
p[i]=min(p[2*id-i],MaxId-i);
else
p[i]=1;
while(st[i+p[i]]==st[i-p[i]])
p[i]++;
if(p[i]+i>MaxId){
id=i;
MaxId=p[i]+i;
}
}
}
int main(){
int res;
scanf("%d",&res);
while(res--){
scanf("%s",&d[1]);
manacher();
int l=0,r=0;
for(int i=1;i<=n;i++){
if(p[i]==i&&i!=1)///p[i]==i保证p[i]可以作为第一个回文串的半径,加入ll数组,i!=1保证第一个回文串不为空
ll[l++]=p[i];
if(p[i]+i-1==n&&i!=n)///与上面类似
rr[r++]=p[i];
}
int i,j;
for(i=l-1;i>=0;--i){
for(j=0;j<=r-1;++j){///枚举第一个回文串和第三个回文串
int tl=2*ll[i];///第二个字符串的开始位置
int tr=n+1-2*rr[j];///第二个字符串的结束位置
int tmp=(tl+tr)/2;///第二个字符串的中间位置,ps:这三个字符串都为奇数,可以自己想想
if(tl>tr) continue;
if(p[tmp]==1) continue;///第二个字符串为"#",也即是在原串中为空
if(p[tmp]*2-1>=tr-tl+1)///以tmp为中点的回文串的长度大于等于第二个字符串的长度,符合题意跳出循环
break;
}
if(j<=r-1)
break;
}
if(i>=0)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}