Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P4555 [国家集训队]最长双回文串

P4555 [国家集训队]最长双回文串

类似的题目好像KMP也做过,先做一次马拉车。记 $f _x$ 为从 $x$ 字符开始的最长的回文字符串,$g _ x$ 为从 $x$ 结束的最长的回文字符串。根据马拉车的答案,找到最长的回文字符串的开始和结尾,得到一个 $f_x$ ,再在此基础上进行递推,如 $f _ x = max(f_x, f _ {x - 1} - 2)$ ,即首尾各减少一个。最后枚举所有分割点得到答案。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <cstdio>
#include <algorithm>
#include <string>
using namespace std;
const int N = 2e5 + 10;
char s[N];
int n, d[N], f[N], g[N];
void init()
{
s[n++] = '$', s[n++] = '#';
char c = getchar();
while (c < 'a' || c > 'z')
c = getchar();
while (c >= 'a' && c <= 'z')
{
s[n++] = c;
s[n++] = '#';
c = getchar();
}
s[n++] = '^';
}
int main()
{
init();
for (int i = 0, l = 0, r = -1; i < n; i++)
{
int k = 1;
if (i <= r)
k = min(d[l + r - i], r - i + 1);
while (i >= k && i + k < n && s[i - k] == s[i + k])
k++;
d[i] = k;
if (i + k - 1 > r)
{
l = i - k + 1;
r = i + k - 1;
}
}
for (int i = 0; i < n; i++)
{
f[i - d[i] + 2 >> 1] = max(f[i - d[i] + 2 >> 1], d[i] - 1);
g[i + d[i] - 2 >> 1] = max(g[i + d[i] - 2 >> 1], d[i] - 1);
}
for (int i = 2, mx = f[1]; i <= (n - 2 >> 1); i++)
f[i] = mx = max(mx - 2, f[i]);
for (int i = (n - 2 >> 1) - 1, mx = g[(n - 2 >> 1)]; i; i--)
g[i] = mx = max(mx - 2, g[i]);
int res = 2;
for (int i = 2; i <= (n - 2 >> 1); i++)
res = max(res, g[i - 1] + f[i]);
printf("%d", res);
return 0;
}