您的位置:首页 > 运维架构

SHTSC2011(SHOI) 双倍回文 一道用Manacher优化的动态维护题

2016-04-11 20:54 429 查看

题目大意

给你一个长度为NN的字符串,求它的最长双倍回文字串。

双倍回文字串:记xx倒置的串为xRx^R,而双倍回文串是能表示成x+xR+xR+xx + x^R + x^R +x形式的字符串。

N≤500000N \leq 500000

解题思路

根据双倍回文串的可知它的长度一定是4的倍数,且由两个相同的有偶数个字符的回文串构成,我们可以先用Manacher与预处理出每一个位置的最长回文半径RiR_i。那么我们设这个串的对称轴是在第pp个字符和第p+1p + 1个字符之间(即pp为第一个xRx^R结尾的位置),qq为第二个xRx^R结尾的位置。那么为了满足双倍回文串的定义q−Rq<=pq - R_q <= p且q<=p+Rp/2q <= p + R_p / 2(这个画一画就能看出来)即只要满足这个条件的一对p,p, qq就可以更新答案。

我们把q−Rqq - R_q排个序,我们在枚举pp时就维护满足条件一qq,把它们加入队列。由于pp递增,所以加到队列的qq就不会出队了。因为答案等于len(p,q)∗4len(p,q) * 4,所以我们只要在队列中找到p+Rp/2p + R_p / 2前面的第一个点就可以了找到对于当前pp的最优解。这里可以用STL维护,也可以用并查集维护。

程序

这个代码为了简洁,用了STL库,跑的有点慢,跑全是aa的极限数据可能会被卡。其实维护前面的第一个点可以用并查集来维护。

//SHTSC2011 Double Palindrome String YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>

using namespace std;

const int MAXN = 5e5 + 5;

set<int> D;

char S[MAXN];
int N, R[MAXN], Ord[MAXN];

bool cmp(int a, int b) { return a - R[a] < b - R[b];}

void Manacher() {
int Max = 0, Id;
for (int i = 1; i <= N; i ++) {
R[i] = (Max >= i) ? min(Max - i, R[2 * Id - 1]) : 0;
for (; S[i + R[i] + 1] == S[i - R[i]]; R[i] ++);
if (R[i] + i > Max) Max = R[i] + i, Id = i;
}
}

void Prepare() {
Manacher();
for (int i = 1; i <= N; i ++) Ord[i] = i;
sort(Ord + 1, Ord + 1 + N, cmp);
}

void Solve() {
int Last = 0, Ans = 0;
for (int i = 1; i <= N; i ++) {
while (Last <= N && Ord[Last] - R[Ord[Last]] <= i) D.insert(Ord[Last ++]);
set<int> :: iterator Side = D.upper_bound(i + R[i] / 2);
if (Side == D.begin()) continue;
Ans = max(Ans, *(--Side) - i);
}
printf("%d", Ans * 4);
}

bool Check() {
for (int i = 2; i <= N; i ++)
if (S[i] != S[i - 1]) return 0;
printf("%d", N / 4 * 4);
return 1;
}

int main() {
scanf("%d", &N);
scanf("%s", S + 1);
if (Check()) return 0;
Prepare();
Solve();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: