求出一个连续子序列,这个子序列由三部分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 }