[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;
}

 

posted @ 2017-03-16 07:35  xjr01  阅读(128)  评论(0编辑  收藏  举报