关闭

COI2016 Palinilap(manacher+后缀数组)

218人阅读 评论(0) 收藏 举报
分类:

题意:给出一个小写字母组成的字符串,修改一个字符(或者不改),使得字符串中所有回文串的长度之和最大(本质相同的回文串算多个)。

好神的题。。题意简单易懂但是做起来非常麻烦。。

以后如果只是求求lcp之类的最好写二分+hash,写个后缀数组简直是自讨苦吃。。

很显然回文串的长度之和就是manacher算法中半径数组之和。

修改一个字符有可能破坏一些回文串,也有可能引入一些新的回文串,只要我们能对每个字符处理出这两个值即可。

先考虑破坏的。考虑一个点,以它为中心扩展出的极大回文串内如果有字母被修改,显然会损失这个点的半径值,且损失程度为从中间向两边递减的公差为1的等差数列。可以用线段树来加,也可以离线+前缀和。

再考虑增加的。对一个点而言,记l和r分别为这个点所能扩展的极大回文串的左右端点。如果l=1或者r=n,则显然这个点半径值无论如何也不能增大了。否则要让这个点的半径值增大,就必须将s[l-1]改为s[r+1]或者将s[r+1]改为s[l-1],修改之后这个点的半径值就可以增加s[r+1]向右和s[l-1]向左所能匹配的最大长度。这个可以将原串逆序后放在后面求一次后缀数组之和用lcp来实现。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const double alp = 0.75;
const LL INF = 1ll<<61;
const int MAXN = 200005;

int N;
char s[MAXN];
int olen[MAXN], elen[MAXN];
void manacher()
{
	static char tmp[MAXN*2];
	static int p[MAXN];
	int M = N*2 + 1;
	for (int i = 0; i<M; ++i) tmp[i] = '#';
	for (int i = 0; i<N; ++i) tmp[i*2+1] = s[i+1];
	for (int i = 0, j = 0, k; i<M; )
	{
		while (i-j>=0 && i+j<M && tmp[i-j]==tmp[i+j]) ++j;
		p[i] = j;
		for (k = 1; p[i-k] < p[i]-k; ++k) p[i+k] = p[i-k];
		i += k, j = max(0, j-k);
	}
	rep(i, 1, N) olen[i] = p[(i-1)*2+1]>>1;
	rep(i, 1, N) elen[i-1] = p[(i-1)*2]>>1;
}

int sa[MAXN], rnk[MAXN], height[MAXN], f[20][MAXN], lg2[MAXN];
#define lch(a) tr[a].ch[0]
#define rch(a) tr[a].ch[1]
int ncnt, rt, len, gt, gtf, tmr;
LL lpos, rpos;
struct Node {
	int ch[2], c, sa, sz;
	LL key;
} tr[MAXN];
int pt[MAXN], pn;
inline int cmp(int x, int y)
{
	if (tr[x].c^tr[y].c) return tr[x].c < tr[y].c;
	return tr[x-1].key < tr[y-1].key;
}
inline void pushup(int x)
{
	tr[x].sz = tr[lch(x)].sz + tr[rch(x)].sz + 1;
}
inline bool isbad(int x)
{
	return max(tr[lch(x)].sz, tr[rch(x)].sz) > tr[x].sz*alp;
}
void treavel(int x)
{
	if (!x) return;
	treavel(lch(x)), pt[++pn] = x, treavel(rch(x));
}
int build(int L, int R, LL l, LL r)
{
	if (L>R) return 0;
	int md = (L+R)>>1;
	int x = pt[md];
	tr[x].key = l+r;
	LL mid = (l+r)>>1;
	lch(x) = build(L, md-1, l, mid);
	rch(x) = build(md+1, R, mid, r);
	return pushup(x), x;
}
void ins(int&x, LL l, LL r)
{
	if (!x) { x = ncnt; tr[x].key = l+r; return; }
	LL mid = (l+r)>>1;
	int d = cmp(x, ncnt);
	if (!d) ins(lch(x), l, mid);
	else ins(rch(x), mid, r);
	if (pushup(x), isbad(x)) gt = x, gtf = 0, lpos = l, rpos = r;
	else if (gt==lch(x)||gt==rch(x)) gtf = x;
}
void extend(int c, int p)
{
	++ncnt, tr[ncnt].sa = p, tr[ncnt].c = c;
	gt = pn = 0, ins(rt, 1, INF);
	if (!gt) return;
	treavel(gt);
	if (!gtf) rt = build(1, pn, 1, INF);
	else if (gt==lch(gtf)) lch(gtf) = build(1, pn, lpos, rpos);
	else rch(gtf) = build(1, pn, lpos, rpos);
}
void dfs(int u)
{
	if (!u) return;
	dfs(lch(u)), sa[++tmr] = tr[u].sa, dfs(rch(u));
}
void MakeST()
{
	rep(i, 1, N) f[0][i] = height[i];
	rep(j, 1, 18) rep(i, 1, N-(1<<j)+1) f[j][i] = min(f[j-1][i], f[j-1][i+(1<<(j-1))]);
}
inline int quary(int l, int r)
{
	int t = lg2[r - l + 1];
	return min(f[t][l], f[t][r - (1<<t) + 1]);
}
inline int lcp(int a, int b)
{
	if (a == b) return N*2 - a + 1;
	a = rnk[a], b = rnk[b];
	if (a > b) swap(a, b);
	return quary(a+1, b);
}
void makesa()
{
	rep(i, 1, N) extend(s[i], 2*N-i+1);
	erp(i, N, 1) extend(s[i], i);
	dfs(rt);
	rep(i, 1, N) s[i+N] = s[N-i+1];
	N*=2;
	rep(i, 1, N) rnk[sa[i]] = i;
	rep(i, 2, N) lg2[i] = lg2[i>>1]+1;
	for (int i = 1, j = 0; i<=N; ++i, j&&--j)
	{
		if (rnk[i]==1) continue;
		while (i+j<=N && sa[rnk[i]-1]+j<=N && s[i+j]==s[sa[rnk[i]-1]+j]) ++j;
		height[rnk[i]] = j;
	}
	MakeST();
	N/=2;
}

LL dv[MAXN], av[26][MAXN];
LL ad[MAXN], adr[MAXN], as[MAXN], asr[MAXN];
void addltr(int l, int r)
{
	ad[l]++, ad[r+1]--;
	as[r+1] -= r-l+1;
}
void addrtl(int l, int r)
{
	l = N-l+1, r = N-r+1, swap(l, r);
	adr[l]++, adr[r+1]--;
	asr[r+1] -= r-l+1;
}
void calsum()
{
	rep(i, 1, N)
	{
		ad[i] += ad[i-1], adr[i] += adr[i-1];
		as[i] += as[i-1], asr[i] += asr[i-1];
	}
	rep(i, 1, N) ad[i] += ad[i-1], adr[i] += adr[i-1];
	reverse(adr+1, adr+N+1);
	reverse(asr+1, asr+N+1);
	rep(i, 1, N) dv[i] = ad[i]+as[i]+adr[i]+asr[i];
}

int getvalue(int l, int r)
{
	if (l<1||r>N) return 0;
	int res = lcp(r, 2*N-l+1);
	res = min(res, min(l, N-r+1));
	return res;
}

void calcLoss()
{
	for (int i = 1, l, r; i<=N; ++i)
	{
		if (olen[i]!=1)
		{
			l=i-olen[i]+1, r=i+olen[i]-1;
			addltr(l, i-1), addrtl(i+1, r);
		}
		if (elen[i]!=0)
		{
			l=i-elen[i]+1, r=i+elen[i];
			addltr(l, i), addrtl(i+1, r);
		}
	}
}

void calcValue()
{
	for (int i = 1, l, r, len; i<=N; ++i)
	{
		l = i-olen[i]+1, r = i+olen[i]-1;
		if (l!=1 && r!=N)
		{
			l --, r ++;
			len = getvalue(l-1, r+1);
			av[s[r]-'a'][l] += len+1;
			av[s[l]-'a'][r] += len+1;
		}
		l = i-elen[i]+1, r = i+elen[i];
		if (l!=1 && r!=N)
		{
			l --, r ++;
			len = getvalue(l-1, r+1);
			av[s[r]-'a'][l] += len+1;
			av[s[l]-'a'][r] += len+1;
		}
	}
}

int main()
{
	scanf("%s", s+1);
	N = strlen(s+1);
	manacher();
	makesa();
	calcLoss();
	calsum();
	calcValue();
	LL sum = 0;
	rep(i, 1, N) sum += elen[i]+olen[i];
	LL ans = sum;
	rep(i, 1, N) rep(j, 0, 25)
		ans = max(ans, sum - dv[i] + av[j][i]);
	cout << ans << '\n';
	return 0;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:48248次
    • 积分:1466
    • 等级:
    • 排名:千里之外
    • 原创:98篇
    • 转载:0篇
    • 译文:0篇
    • 评论:4条
    最新评论