[APIO2014]回文串
嘟嘟嘟
这道题真的挺好的,虽然数据很水,还卡空间。
有多水呢?建完SAM后,把他和反串匹配一遍,就能得90分……这个做法显然是不对的,比如abcweabc,求出来是3,但答案应该是2.
因为我不会回文自动机,所以就学了一下题解的SAM+manacher的做法。
建完SAM后,开始用manacher求回文子串,每求出一个本质不同的回文子串,就把这个子串放到SAM上跑,更新答案。本质不同的回文子串就是令p[i]增长的串(但我现在不是很清楚为啥)。
但这样的复杂度是\(O(n ^ 2)\)的,得优化一下。
找回文串\(O(n)\)是肯定没办法再优化的,只能优化在SAM上跑的过程。
因为每一个子串所属的节点在SAM上是一定存在的,所以我们可以用倍增的方法优化找该节点的过程。
建SAM的时候,记录每一个前缀在parent tree上深度最深的节点,这样这个前缀的所有后缀所在的节点要么是它本身,要么是他的祖先节点。因此预处理倍增数组就OK了。
所以复杂度是\(O(nlogn)\)
因为我把manacher给忘了,所以代码中manacher的部分借鉴了一下hzwer大仙的……
差点忘了,这题就给128MB,所以空间得卡卡,比如转移数组开到tra[maxn][27],而不是tra[maxn][30]……
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
-typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 6e5 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
ll ans = 0;
char s[maxn >> 1];
struct Sam
{
int las, cnt;
int tra[maxn][27], len[maxn], link[maxn], siz[maxn], a[maxn >> 1];
In void init() {link[las = cnt = 0] = -1;}
In void insert(int c, int id)
{
int now = ++cnt, p = las; a[id] = now;
len[now] = len[las] + 1; siz[now] = 1;
while(~p && !tra[p][c]) tra[p][c] = now, p = link[p];
if(p == -1) link[now] = 0;
else
{
int q = tra[p][c];
if(len[q] == len[p] + 1) link[now] = q;
else
{
int clo = ++cnt;
memcpy(tra[clo], tra[q], sizeof(tra[q]));
len[clo] = len[p] + 1;
link[clo] = link[q], link[q] = link[now] = clo;
while(~p && tra[p][c] == q) tra[p][c] = clo, p = link[p];
}
}
las = now;
}
int buc[maxn], pos[maxn], dep[maxn], dp[maxn][20];
In void solve()
{
for(int i = 1; i <= cnt; ++i) ++buc[len[i]];
for(int i = 1; i <= cnt; ++i) buc[i] += buc[i - 1];
for(int i = 1; i <= cnt; ++i) pos[buc[len[i]]--] = i;
for(int i = cnt; i; --i) siz[link[pos[i]]] += siz[pos[i]];
for(int i = 1; i <= cnt; ++i)
{
int now = pos[i];
dep[now] = dep[link[now]] + 1;
dp[now][0] = link[now];
for(int j = 1; (1 << j) <= dep[now]; ++j)
dp[now][j] = dp[dp[now][j - 1]][j - 1];
}
}
In void query(int L, int R)
{
int now = a[R];
for(int i = 18; i >=0; --i)
if(len[dp[now][i]] >= R - L + 1) now = dp[now][i];
ans = max(ans, 1LL * siz[now] * (R - L + 1));
}
}S;
int p[maxn >> 1];
In void manacher(int n)
{
int mx = 0, id;
for(int i = 1; i <= n; ++i) //even
{
if(mx > i) p[i] = min(mx - i, p[(id << 1) - i - 1]);
else p[i] = 0;
while(i - p[i] >= 0 && s[i + p[i] + 1] == s[i - p[i]])
{
++p[i];
S.query(i - p[i] + 1, i + p[i]);
}
if(i + p[i] > mx) mx = i + p[i], id = i;
}
mx = 0; Mem(p, 0);
for(int i = 1; i <= n; ++i) //odd
{
if(mx > i) p[i] = min(mx - i - 1, p[(id << 1) - i]);
else p[i] = 1, S.query(i, i);
while(i - p[i] >= 0 && s[i + p[i]] == s[i - p[i]])
{
++p[i];
S.query(i - p[i] + 1, i + p[i] - 1);
}
if(i + p[i] > mx) mx = i + p[i], id = i;
}
}
int main()
{
scanf("%s", s + 1);
int n = strlen(s + 1); S.init();
s[0] = '!'; s[n + 1] = '@';
for(int i = 1; i <= n; ++i) S.insert(s[i] - 'a', i);
S.solve(); manacher(n);
write(ans), enter;
return 0;
}