Hotaru's problem
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 243 Accepted Submission(s): 61
Problem Description
Hotaru Ichijou recently is addicated to math problems. Now she is playing with N-sequence.
Let's define N-sequence, which is composed with three parts and satisfied with the following condition:
1. the first part is the same as the thrid part,
2. the first part and the second part are symmetrical.
for example, the sequence 2,3,4,4,3,2,2,3,4 is a N-sequence, which the first part 2,3,4 is the same as the thrid part 2,3,4, the first part 2,3,4 and the second part 4,3,2 are symmetrical.
Give you n positive intergers, your task is to find the largest continuous sub-sequence, which is N-sequence.
Let's define N-sequence, which is composed with three parts and satisfied with the following condition:
1. the first part is the same as the thrid part,
2. the first part and the second part are symmetrical.
for example, the sequence 2,3,4,4,3,2,2,3,4 is a N-sequence, which the first part 2,3,4 is the same as the thrid part 2,3,4, the first part 2,3,4 and the second part 4,3,2 are symmetrical.
Give you n positive intergers, your task is to find the largest continuous sub-sequence, which is N-sequence.
Input
There are multiple test cases. The first line of input contains an integer T(T<=20), indicating the number of test cases.
For each test case:
the first line of input contains a positive integer N(1<=N<=100000), the length of a given sequence
the second line includes N non-negative integers ,each interger is no larger than 109 , descripting a sequence.
For each test case:
the first line of input contains a positive integer N(1<=N<=100000), the length of a given sequence
the second line includes N non-negative integers ,each interger is no larger than 109 , descripting a sequence.
Output
Each case contains only one line. Each line should start with “Case #i: ”,with i implying the case number, followed by a integer, the largest length of N-sequence.
We guarantee that the sum of all answers is less than 800000.
We guarantee that the sum of all answers is less than 800000.
Sample Input
1 10 2 3 4 4 3 2 2 3 4 4
Sample Output
Case #1: 9
Source
用manacher算法算出每个分割位置的最长回文子串。
然后枚举每个分割位置
分析题目得到的答案串是
AB|BA|AB 就是两个回文串的拼接,有两个对称中心
对于每个位置i,回文串长度len[i],在[i,i+len[i]]中寻找满足 j-len[j] <= i的最大的j。
那么得到的一组解就是 min(len[i], j-i)
由于manacher算法是在每个位置插入一个符号,所有只计算偶数位即可,偶数位就是分割位置。
用线段树维护查找区间最大解即可。
线段树的结点表示这个位置的j-len[j]
#include<cstring>
#include<cstdio>
#include<string>
#include <iostream>
#include<algorithm>
using namespace std;
#define maxn 800007
int lc[maxn],rc[maxn],val[maxn];
int rad[maxn];
int tmp[maxn];
int s[maxn];
int inf = 1000000000;
void manacher(int n){//求回文串
int cnt = 0;
memset(rad,0,sizeof(rad));
for(int i=0;i<n;i++){
s[cnt++] = inf;
s[cnt++] = tmp[i];
}
s[cnt++] = inf;
n = cnt;
int i=0,j=1,k;
while(i<n){
while(i-j>=0 && i+j<n && s[i-j]==s[i+j])
j++;
rad[i]=j-1;
k=1;
while(k<=rad[i] && rad[i]-k!=rad[i-k]){
rad[i+k]=min(rad[i-k],rad[i]-k);
k++;
}
i+=k;
j=max(j-k,0);
}
}
int tcnt;
//建树,奇数位就是原串的位置,不能作为分割点
void build(int u,int l,int r){
if(l == r){
if(l & 1) val[u] = inf;
else val[u] = l - rad[l];
return ;
}
lc[u] = tcnt++;
rc[u] = tcnt++;
int mid = (l+r)/2;
build(lc[u],l,mid);
build(rc[u],mid+1,r);
val[u] = min(val[lc[u]],val[rc[u]]);
}
//查询在L,R区间满足小于等于p的最右边的位置
int query(int u,int l,int r,int L,int R, int p){
if(l == r) return l;
int mid = (l+r)/2;
if(l == L && R == r){
if(val[u] > p) return inf;
if(val[rc[u]] <= p)
return query(rc[u],mid+1,r,mid+1,R,p);
else if(val[lc[u]] <= p) return query(lc[u],l,mid,l,mid,p);
else return inf;
}
if(mid < L)
return query(rc[u],mid+1,r,L,R,p);
else if(mid >= R)
return query(lc[u],l,mid,L,R,p);
else {
int ans = query(rc[u],mid+1,r,mid+1,r,p);
if(ans != inf) return ans;
return query(lc[u],l,mid,L,mid,p);
}
return inf;
}
void init(){
tcnt = 2;
memset(rc,0,sizeof(rc));
memset(lc,0,sizeof(lc));
memset(val,0x3f,sizeof(val));
}
int main()
{
int t,tt=1,n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i = 0;i < n; i++)
scanf("%d",&tmp[i]);
manacher(n);
n = n*2+1;
init();
build(1,0,n-1);
int ans = 0;
for(int i = 0;i < n; i+=2){ //枚举分割位置
int p = i + rad[i];
int q = query(1,0,n-1,i,p,i);
if(q == inf) continue;
int res = min(rad[i],q-i);
ans = max(ans,res/2);//manacher的长度增加了一倍,所有要/2
}
printf("Case #%d: %d\n",tt++,ans*3);
}
return 0;
}
/*
22
10
2 3 4 4 3 2 2 3 4 4
20
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
12
1 1 2 2
2 2 1 1
1 1 2 2
*/