洛谷传送门
BZOJ传送门
题目描述
顺序和逆序读起来完全一样的串叫做回文串。比如acbca
是回文串,而abc
不是(abc
的顺序为abc
,逆序为cba
,不相同)。
输入长度为 n n n的串 S S S,求 S S S的最长双回文子串 T T T,即可将 T T T分为两部分 X X X, Y Y Y,( ∣ X ∣ , ∣ Y ∣ ≥ 1 |X|,|Y|≥1 ∣X∣,∣Y∣≥1)且 X X X和 Y Y Y都是回文串。
输入输出格式
输入格式:
一行由小写英文字母组成的字符串 S S S。
输出格式:
一行一个整数,表示最长双回文子串的长度。
输入输出样例
输入样例#1:
baacaabbacabb
输出样例#1:
12
说明
【样例说明】
从第二个字符开始的字符串aacaabbacabb
可分为aacaa
与bbacabb
两部分,且两者都是回文串。
对于100%的数据, 2 ≤ ∣ S ∣ ≤ 1 0 5 2≤|S|≤10^5 2≤∣S∣≤105
解题分析
回文自动机板题, 因为回文自动机的 l e n len len指的是以当前点结尾的回文串的最大长度, 所以正反建两个回文自动机, 跑一跑就好了。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MX 200500
struct PAM
{
int len[MX], fail[MX], to[MX][26], str[MX], ans[MX];
int cnt, n, cur, last;
IN int _nw(R int val) {len[cnt] = val; return cnt++;}
IN void reset()
{
cnt = 0, _nw(0), _nw(-1);
n = last = cur = 0; str[0] = -1;
fail[0] = 1;
}
IN int jump(R int now)
{
W (str[n] != str[n - len[now] - 1]) now = fail[now];
return now;
}
IN void insert(R int id)
{
str[++n] = id; R int now = jump(last);
if(!to[now][id])
{
cur = _nw(len[now] + 2);
fail[cur] = to[jump(fail[now])][id];
to[now][id] = cur;
}
last = to[now][id]; ans[n] = len[last];
}
}p1, p2;
char dat[MX];
int l;
int main(void)
{
p1.reset(); p2.reset();
scanf("%s", dat + 1); l = std::strlen(dat + 1);
for (R int i = 1; i <= l; ++i) p1.insert(dat[i] - 'a');
std::reverse(dat + 1, dat + 1 + l);
for (R int i = 1; i <= l; ++i) p2.insert(dat[i] - 'a');
int ans = 0;
for (R int i = 1; i <= l; ++i) ans = std::max(ans, p1.ans[i] + p2.ans[l - i]);
printf("%d", ans);
}