2342: [Shoi2011]双倍回文
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 4560 Solved: 1824
[Submit][Status][Discuss]
Description
双倍回文串的定义为一个长度为4的倍数的回文串,它的左半部分与右半部分都是一个回文串。
求最长双倍回文串。 N<=500000。
Input
输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容。
Output
输出文件只有一行,即:输入数据中字符串的最长双倍回文子串的长度,如果双倍回文子串不存在,则输出0。
Sample Input
16
ggabaabaabaaball
Sample Output
12
HINT
N<=500000
分析:首先跑一遍回文树,然后遍历长度,对于len[i]%4==0的回文串进行check,若这个回文串的左子串和右子串相同,那么就是双回串,更新答案。check直接hash一下就可以了。
#include <bits/stdc++.h>
using namespace std;
const int N = 500000 + 4;
const int base = 233;
char s[N];
unsigned long long Hash[N], pre[N];
unsigned long long getH(int l, int r) {
if (l == 0)return Hash[r];
else return Hash[r] - Hash[l - 1] * pre[r - l + 1];
}
bool check(int l, int r) {
int mid = (l + r) >> 1;
unsigned long long a = getH(l, mid), b = getH(mid + 1, r);
return (a == b);
}
struct Palindromic_Tree {
int nxt[N][30], fail[N], cnt[N];
int num[N], len[N], s[N], id[N];
int last, n, p;
int newnode(int l) {
memset(nxt[p], 0, sizeof(nxt[p]));
cnt[p] = num[p] = 0;
len[p] = l;
return p++;
}
void init() {
p = 0;
newnode(0), newnode(-1);
last = n = 0;
s[0] = -1;
fail[0] = 1;
}
int get_fail(int x) {
while (s[n - len[x] - 1] != s[n]) x = fail[x];
return x;
}
void add(int c) {
c -= 'a';
s[++n] = c;
int cur = get_fail(last);
if (!nxt[cur][c]) {
int now = newnode(len[cur] + 2);
fail[now] = nxt[get_fail(fail[cur])][c];
nxt[cur][c] = now;
num[now] = num[fail[now]] + 1;
}
last = nxt[cur][c];
cnt[last]++, id[last] = n;
}
int rush() {
int ans = 0;
for (int i = 2; i < p; ++i) {
if (len[i] % 4 == 0 && check(id[i] - len[i], id[i] - 1))ans = max(ans, len[i]);
}
return ans;
}
} pam;
int main() {
int n;
pre[0] = 1;
for (int i = 1; i < N; ++i) {
pre[i] = pre[i - 1] * base;
}
while (cin >> n >> s) {
Hash[0] = s[0] - 'a';
for (int i = 1; i < n; ++i) {
Hash[i] = Hash[i - 1] * base + s[i] - 'a';
}
pam.init();
for (int i = 0; i < n; ++i) {
pam.add(s[i]);
}
cout << pam.rush() << endl;
}
}
/*
abaaba abaaba*/