您的位置:首页 > 其它

HDU 5371 Manacher Hotaru's problem

2015-08-15 16:51 120 查看
求出一个连续子序列,这个子序列由三部分ABC构成,其中AB是回文串,A和C相同,也就是BC也是回文串。

求这样一个最长的子序列。

Manacher算法是在所有两个相邻数字之间插入一个特殊的数字,比如-1,

Manacher算法跑完之后,就计算出每个数字为中心的回文子序列的最大长度

由题意可以知道,AB和BC必然是长度为偶数的回文串。所以我们枚举回文串的中心就枚举相邻两个数字之间的缝隙,也就是那些-1

把AB中间的间隙叫做左中心i,BC之间的间隙叫做右中心j,那么如果两个中心的范围能够互相覆盖,那么就找到一个符合条件的连续子序列。

做法就是枚举左中心i,在左中心的范围内枚举右中心j,然后维护一个最大值即可。

在枚举j的时候不要直接从[i+1, i + p[i] - 1]枚举,会超时的。

比如说我们维护的最大值是ans,那么直接从 i + ans 开始枚举,因为之前的区间即使找到合法子序列也并不能更新这个最大值。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 100000 + 10;

int n, tot;
int a[maxn], b[maxn * 2];

int p[maxn * 2];

void Manacher()
{
int id, mx = 0;
p[0] = 0;
for(int i = 1; i < tot; i++)
{
if(mx > i) p[i] = min(p[id * 2 - i], mx - i);
else p[i] = 1;
while(b[i + p[i]] == b[i - p[i]]) p[i]++;
if(i + p[i] > mx) { mx = i + p[i]; id = i; }
}
}

int main()
{
int T; scanf("%d", &T);
for(int kase = 1; kase <= T; kase++)
{
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", a + i);

b[0] = -2, b[1] = -1;
tot = 2;
for(int i = 0; i < n; i++)
{
b[tot++] = a[i];
b[tot++] = -1;
}

Manacher();

int ans = 1;
for(int i = 3; i < tot; i += 2)
{
for(int j = ans; j <= p[i]; j += 2)
if(p[i + j - 1] >= j) ans = j;
}

ans = ans / 2 * 3;
printf("Case #%d: %d\n", kase, ans);
}

return 0;
}


代码君
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: