题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5340
题目大意:给你一个全部都是小写英文字母的字符串 s,|s|<20000 问你能不能分成3个非空的回文字串。
解题思路:Manacher算法O(n)求出以每个点为中心的最大的回文的半径,然后暴力枚举分割位置,验证!
我之前用hash写过一发,一直TLE,发现是我的hash写挫了,本来可以O(n)的预处理 ,O(1)的查询,但是我写成了O(n)的预处理,O(logn)的查询,然后一直TLE。
最后还是问师兄,才知道怎么写O(1)的查询,我真是太弱了。。。
//#pragma comment(linker,"/STACK:102400000,102400000")
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<string>
#define ll long long
#define db double
#define PB push_back
#define lson k<<1
#define rson k<<1|1
using namespace std;
const int N = 20005;
char str[N], nstr[N << 1];
int R[N << 1];
int len, nlen;
void Manacher()
{
nstr[0] = '#';
nlen = 1;
for(int i = 0; i < len; i++)
{
nstr[nlen++] = str[i];
nstr[nlen++] = '*';
}
nstr[nlen++] = '#';
int p(0);
for(int i = 1; i < nlen; i++)
{
if(2 * p - i >= 0) R[i] = min(p + R[p] - i, R[2 * p - i]);
else R[i] = p + R[p] - i;
R[i] = max(R[i], 0);
while(i + R[i] + 1 < nlen && i - R[i] - 1 >= 0 && nstr[i + R[i] + 1] == nstr[i - R[i] - 1]) R[i]++;
if(R[i] > R[p]) p = i;
}
}
int pre[N], lp;
int suf[N], ls;
bool check(int l, int r)
{
int mid = (l + r) >> 1;
if(R[mid] >= mid - l) return true;
return false;
}
int main()
{
#ifdef PKWV
// freopen("in.in", "r", stdin);
#endif // PKWV
int T;
scanf("%d", &T);
while(T--)
{
scanf("%s", str);
len = strlen(str);
Manacher();
lp = 0, ls = 0;
for(int i = len - 1; i >= 0; i--)
{
int p = i * 2 + 1;
if(check(1,p)) pre[lp++]=i;
if(check(p,len*2-1)) suf[ls++]=i;
}
bool ok(false);
for(int i = 0; i < lp; i++)
{
for(int j = 0; !ok && j < ls && pre[i] + 1 < suf[j]; j++)
{
if(check((pre[i] + 1)<< 1 | 1, (suf[j] - 1) << 1 | 1))
{
ok = true;
break;
}
}
}
if(ok) printf("Yes\n");
else printf("No\n");
}
return 0;
}