[国家集训队] 最长双回文串
P4555 [国家集训队] 最长双回文串
题目描述
顺序和逆序读起来完全一样的串叫做回文串。比如 acbca
是回文串,而 abc
不是:abc
的顺序为 abc
,逆序为 cba
,不相同。
输入长度为 n n n 的串 S S S,求 S S S 的最长双回文子串 T T T,即可将 T T T 分为两部分 X , Y X, Y X,Y( ∣ X ∣ , ∣ Y ∣ ≥ 1 |X|,|Y|≥1 ∣X∣,∣Y∣≥1)且 X X X 和 Y Y Y 都是回文串。
输入格式
一行由小写英文字母组成的字符串 S S S。
输出格式
一行一个整数,表示最长双回文子串的长度。
样例 #1
样例输入 #1
baacaabbacabb
样例输出 #1
12
提示
样例说明
从第二个字符开始的字符串 aacaabbacabb
可分为 aacaa
与 bbacabb
两部分,且两者都是回文串。
数据范围
对于 100 % 100\% 100% 的数据, 2 ≤ ∣ S ∣ ≤ 1 0 5 2\leq |S|\leq 10^5 2≤∣S∣≤105。
2018.12.10,2018.12.15:感谢 @Ycrpro 提供 hack 数据两组。
分析:这个题有点麻烦,首先有一个比较重要的思维,求一个串由两个回文子串构成,那么我们可以这样想:如果我们知道以s[i]为端点,左边的最长回文子串和右边的最长回文子串,这样两个回文串拼起来就是一个双回文串,所以我们可以试去想有没有办法知道每个以i为端点的左边的和右边的最长回文子串;当然是可以的。
详细可以看这个题解 P4555 【国家集训队最长双回文串】
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int p[N << 1];
char a[N], ma[N << 1];
int l[N << 1], r[N << 1];
void manacher(char s[], int len) {
int k = 0;
ma[k++] = '$';
ma[k++] = '#';
for (int i = 0; i < len; i++) {
ma[k++] = s[i];
ma[k++] = '#';
}
// cout << ma << endl;
int mr = 0, c = 0;
for (int i = 1; i < k; i++) {
if (i < mr) {
p[i] = min(p[(c << 1) - i], p[c] + c - i);
} else {
p[i] = 1;
}
while (ma[p[i] + i] == ma[i - p[i]])
p[i]++;
if (p[i] + i > mr) {
mr = p[i] + i;
c = i;
}
l[i + p[i] - 1] = max(l[i + p[i] - 1], p[i] - 1);
r[i - p[i] + 1] = max(r[i - p[i] + 1], p[i] - 1);
}
}
int main() {
cin >> a;
int len = strlen(a);
manacher(a, len);
// cout << ' ';
for (int i = 1; i <= len * 2 + 1; i += 2) {
r[i] = max(r[i], r[i - 2] - 2);
// cout << r[i] << ' ';
}
// cout << endl;
// cout << ' ';
for (int i = len * 2 + 1; i >= 1; i -= 2) {
l[i] = max(l[i], l[i + 2] - 2);
// cout << l[i] << ' ';
}
// cout << endl;
int ans = 0;
for (int i = 1; i < len * 2 + 1; i += 2) {
if (r[i] && l[i]) {
ans = max(ans, r[i] + l[i]);
}
}
cout << ans << endl;
}