给一个串,问是否能把它拆为三个回文串。
用Manachar计算以每个字符为中心的最长回文串长度,然后枚举头尾,判断中心是否为回文串。
理解了一下manachar,它从左往右扫,记录每个位置为中心的最长回文串长度,维护了当前回文串能到达的最右边的位置和回文串长度、中心。根据这些信息以及对称性,就可以推知下一个位置回文长度至少是多少。虽然有两重循环,但内循环只会执行O(n)次,因为当前回文串能到达的最右边是单调递增的。
官方题解给的二进制压位优化没有弄明白,求大神解释。。不压位写好点也是能过的。
然后一个学弟把压位看明白了,其实想明白就简单了。。就是把32个0或1的位压到一个int里,一次性比较32位。。优化常数。。就不实现了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <stack>
using namespace std;
const int MAX = 20010;
int len, p[2*MAX];
char str[2*MAX], newstr[2*MAX];
void change(){
int i;
newstr[0] = '@';
newstr[1] = '#';
for (i = 0; i < len; i++){
newstr[2*i + 2] = str[i];
newstr[2*i + 3] = '#';
}
newstr[2*len + 2] = '\0';
return ;
}
void Manacher(){
int i, j, id, maxid = 0, ans = 1;
len = 2 * len + 2;
for (i = 0; i < len; i++){
if (maxid > i){
p[i] = min(p[2*id - i], maxid - i);
}
else{
p[i] = 1;
}
while (newstr[i+p[i]] == newstr[i-p[i]])
p[i]++;
if (p[i] + i > maxid){
maxid = p[i] + i;
id = i;
}
if (ans < p[i])
ans = p[i];
}
return ;
}
int head[MAX];
int tail[MAX];
int main(){
int t;
cin>>t;
while (t--){
scanf("%s", &str);
len = strlen(str);
change();
Manacher();
int headCnt=0;
int tailCnt=0;
for(int i=2;i<=len-2;i++){
if(p[i]==i){
head[headCnt++]=i;
}
if(p[i]==len-i){
tail[tailCnt++]=i;
}
}
bool ok=0;
for(int i=0;i<headCnt;i++){
for(int j=0;j<tailCnt;j++){
int l=head[i]+p[head[i]] ;
int r=tail[j]-p[tail[j]] ;
if(l>r)continue;
int mid = (head[i]+p[head[i]] + tail[j]-p[tail[j]])>>1;
if(l==r&&newstr[mid]=='#')continue;
if(p[head[i]]*2-1+p[tail[j]]*2-1+p[mid]*2-1>=len+1){
ok=1;break;
}
}
if(ok)break;
}
if(ok){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
return 0;
}