COI2016 Palinilap(manacher+后缀数组)

原创 2016年05月31日 22:04:18

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

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

以后如果只是求求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;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

Hdu 5785 Interesting(Manacher+区间处理)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5785 思路: 1.L[i]表示以i开始的所有回文的结束位置和,R[i]表示以i位置结尾的所有回文开始位...
  • wang2147483647
  • wang2147483647
  • 2017年09月06日 22:32
  • 346

五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)

这是一篇本人自己对后缀数组的一些理解,有详细的说明以及附有详解的代码。...
  • YxuanwKeith
  • YxuanwKeith
  • 2016年02月05日 13:13
  • 17630

后缀数组:原理和实现

后缀数组(Suffix Array)是某一字符串的所有后缀按照字典序的一个排列。本文数组的索引从0开始。称s[j..len(s)-1]为后缀j。sa[i] = j,表示原串的所有后缀按字典序排列,排在...
  • ruoruo_cheng
  • ruoruo_cheng
  • 2016年08月21日 21:47
  • 1825

后缀数组罗穗蹇模板(dc3)

#include #include #include #include #define maxn 1000003 #define F(x) ((x)/3+((x)%3==1?0:tb)) #defin...
  • u013665921
  • u013665921
  • 2014年10月14日 09:52
  • 1396

后缀数组倍增法

那夜风,宫灯昏暗,小楼听雨灯辉摇梦
  • reverie_mjp
  • reverie_mjp
  • 2016年04月17日 22:04
  • 496

后缀数组详解

转载自 : http://blog.csdn.net/j_sure/article/details/41777097 后缀数组学习笔记【详解】 老天,一个后缀数组不知道看了多少天,最后...
  • qq_34731703
  • qq_34731703
  • 2016年10月26日 14:50
  • 3566

实用算法实现-第8篇 后缀树和后缀数组 [1简介]

8.1    后缀树 一棵后缀树包含一个指定文本的所有后缀,对于在一个长度为N的文本中查找一个长度为M的子字符串,一个后缀树仅仅需要M次比较,而这个比较次数是查找该字符串所需要的最小比较次数。 后...
  • fsdev
  • fsdev
  • 2011年10月15日 11:50
  • 3816

HDU 1403 Longest Common Substring(后缀数组入门)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1403拖了这么久的后缀数组,终于准备学一学了。 做的第一题(套的第一个板子),虽然还是没看不懂是怎么实现...
  • xtttgo
  • xtttgo
  • 2016年08月14日 10:28
  • 263

后缀数组练习题若干

POJ 1743    不可重叠最长重复子串 二分答案。 即子串的长度,假设为k时。 利用height数组,将排序后的后缀分为若干组。 每组内的height值都不小于k。 然后只需查看组内是否有满足要...
  • sdj222555
  • sdj222555
  • 2013年10月13日 21:00
  • 2485

后缀数组——罗穗骞倍增算法代码详解

首先解释一下用到的几个数组。 数组sa:构造完成前表示关键字数组,下标表示名次,值表示关键字的首字符位置,值相同的时候名次根据在原串中相对位置的先后决定;构造完成后表示后缀数组,下标表示名次,值表示...
  • rockzh1993
  • rockzh1993
  • 2015年12月19日 10:28
  • 1913
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:COI2016 Palinilap(manacher+后缀数组)
举报原因:
原因补充:

(最多只允许输入30个字)