HDU 5371 Manacher Hotaru's problem

求出一个连续子序列,这个子序列由三部分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 开始枚举,因为之前的区间即使找到合法子序列也并不能更新这个最大值。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int maxn = 100000 + 10;
 8 
 9 int n, tot;
10 int a[maxn], b[maxn * 2];
11 
12 int p[maxn * 2];
13 
14 void Manacher()
15 {
16     int id, mx = 0;
17     p[0] = 0;
18     for(int i = 1; i < tot; i++)
19     {
20         if(mx > i) p[i] = min(p[id * 2 - i], mx - i);
21         else p[i] = 1;
22         while(b[i + p[i]] == b[i - p[i]]) p[i]++;
23         if(i + p[i] > mx) { mx = i + p[i]; id = i; }
24     }
25 }
26 
27 int main()
28 {
29     int T; scanf("%d", &T);
30     for(int kase = 1; kase <= T; kase++)
31     {
32         scanf("%d", &n);
33         for(int i = 0; i < n; i++) scanf("%d", a + i);
34 
35         b[0] = -2, b[1] = -1;
36         tot = 2;
37         for(int i = 0; i < n; i++)
38         {
39             b[tot++] = a[i];
40             b[tot++] = -1;
41         }
42 
43         Manacher();
44 
45         int ans = 1;
46         for(int i = 3; i < tot; i += 2)
47         {
48             for(int j = ans; j <= p[i]; j += 2)
49                 if(p[i + j - 1] >= j) ans = j;
50         }
51 
52         ans = ans / 2 * 3;
53         printf("Case #%d: %d\n", kase, ans);
54     }
55 
56     return 0;
57 }
代码君

 

转载于:https://www.cnblogs.com/AOQNRMGYXLMV/p/4732720.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值