[BZOJ3676][Apio2014]回文串
[BZOJ3676][Apio2014]回文串
试题描述
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。
输入
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。
输出
输出一个整数,为逝查回文子串的最大出现值。
输入示例
abacaba
输出示例
7
数据规模及约定
数据满足1≤字符串长度≤300000。
题解
首先我们用 manacher 求出所有极长的回文子串,然后通过这个我们可以求出以每个位置 i 为结尾,向左延伸到的最长回文子串长度是多少(记为 len[i])。方法就是先对于极长的回文子串打一下标记,然后从后往前扫一遍,每往前移一个位置值减一,然后和当前位置的标记取个 max。
接下来我们构造后缀自动机,对于每个状态的 right 集合,我们只要知道这个集合的大小就知道当前串出现了几次;现在我们还需要保证所有找到的串是回文的,于是就可以用上面求出来的 len[i] 做。不难发现对于一个状态 u 的 right 集合,把集合内所有位置上的 len[i] 取 min(注意判断这个 min 值必须要小于等于 Max[u])就是当前节点的答案了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define maxn 600010 #define maxnode 600010 #define maxa 26 #define oo 2147483647 #define LL long long char S[maxn], Str[maxn]; int n, Len[maxn], len[maxn], alp[maxn]; int rt, last, ToT, to[maxnode][maxa], par[maxnode], Max[maxnode], val[maxnode], mnl[maxnode]; void extend(int pos) { int x = Str[pos] - 'a', p = last, np = ++ToT; Max[np] = Max[p] + 1; val[np] = 1; mnl[np] = len[pos]; last = np; while(p && !to[p][x]) to[p][x] = np, p = par[p]; if(!p){ par[np] = rt; return ; } int q = to[p][x]; if(Max[q] == Max[p] + 1){ par[np] = q; return ; } int nq = ++ToT; Max[nq] = Max[p] + 1; mnl[nq] = oo; memcpy(to[nq], to[q], sizeof(to[q])); par[nq] = par[q]; par[q] = par[np] = nq; while(p && to[p][x] == q) to[p][x] = nq, p = par[p]; return ; } int sa[maxnode], Ws[maxnode]; int main() { scanf("%s", Str + 1); n = strlen(Str + 1); for(int i = 1; i <= n; i++) S[(i<<1)-1] = Str[i], S[i<<1] = 'z' + 1; n <<= 1; for(int i = 1; i <= n; i++) alp[i] = alp[i-1] + (isalpha(S[i]) ? 1 : 0); int mxp = 0; for(int i = 1; i <= n; i++) { int mxr = mxp + Len[mxp] - 1; if(mxr >= i) Len[i] = min(Len[(mxp<<1)-i], mxr - i + 1); else Len[i] = 1; while(1 <= i - Len[i] + 1 && i + Len[i] - 1 <= n && S[i-Len[i]+1] == S[i+Len[i]-1]) Len[i]++; Len[i]--; if(mxr < i + Len[i] - 1) mxp = i; } for(int i = 1; i <= n; i++) len[i+Len[i]-1] = max(len[i+Len[i]-1], Len[i]); // for(int i = 1; i <= n; i++) printf("%d%c", Len[i], i < n ? ' ' : '\n'); // for(int i = 1; i <= n; i++) printf("%d%c", len[i], i < n ? ' ' : '\n'); int mx = 1; for(int i = n; i; i--) { mx--; if(len[i] > mx) mx = len[i]; else len[i] = mx; } // for(int i = 1; i <= n; i++) printf("%d%c", len[i], i < n ? ' ' : '\n'); for(int i = 1; i <= n; i++) { len[i] = (len[i] << 1) - 1; int l = len[i]; len[i] = alp[i] - alp[i-l]; } for(int i = 1; i <= (n >> 1); i++) len[i] = max(len[(i<<1)-1], len[i<<1]); n >>= 1; // puts(S + 1); // for(int i = 1; i <= n; i++) printf("%d%c", len[i], i < n ? ' ' : '\n'); rt = last = ToT = 1; for(int i = 1; i <= n; i++) extend(i); for(int i = 1; i <= ToT; i++) Ws[n-Max[i]]++; for(int i = 1; i <= n; i++) Ws[i] += Ws[i-1]; for(int i = ToT; i; i--) sa[Ws[n-Max[i]]--] = i; LL ans = 0; for(int i = 1; i <= ToT; i++) { int u = sa[i]; mnl[par[u]] = min(mnl[par[u]], mnl[u]); val[par[u]] += val[u]; if(Max[u] >= mnl[u]) ans = max(ans, (LL)mnl[u] * val[u]); } printf("%lld\n", ans); return 0; }