bzoj 3790 神奇项链 回文串 manacher|PAM

LINK:神奇项链

存在两个操作:1. 一个操作可以生成所有形式的回文串 2.一个操作可以将两个串给合并起来 如果前缀和后缀相同还可以将其并起来。

多组询问 每次询问合成一个串所需最少多少次2操作。

不考虑1操作 那么我们可以把所有需要的串都给生成出来 对于原字符串没有贡献的当然是不需要的了。

可以发现 当原字符串有一部分不是回文但是前一部分和后一部分用的同一个地方都成为了回文 此时也是可以进行2操作的。

考虑从前往后构造当前面一部分构造好了后面无论占用前面多大位置都没有问题。

转换成区间覆盖问题。问题是我们要覆盖区间的线段怎么找。原串中的回文串。

可以发现越大越好 对于每个点找到最长的回文串进行区间覆盖问题即可。

PAM解决(忘了 Manacher解决吧。

求出区间之后 可以排序+贪心解决。

当然 可以考虑求出以某个节点为左端的的最长右端点这个复杂度为O(n)也就自然省掉了排序的过程+贪心可以达到O(n).

也可以暴力求出所有区间然后排序。

最后的贪心:可以dp来做 但是需要数据结构优化一下 直接贪心考虑左端点一定往后延伸最长右端点。

const int MAXN=50010;
int n,cnt,id;
int p[MAXN<<1];
char a[MAXN],b[MAXN<<1];
struct wy
{
	int x,y;
	inline int friend operator <(wy a,wy b){return a.x<b.x;}
}t[MAXN<<1];
inline void manacher()
{
	int mx=0,mid=0;
	rep(1,cnt,i)
	{
		if(i<mx)p[i]=min(p[(mid<<1)-i],mx-i);
		else p[i]=1;
		while(b[i-p[i]]==b[i+p[i]])++p[i];
		if(p[i]+i>mx)mx=p[i]+i,mid=i;
	}
}
int main()
{
	freopen("1.in","r",stdin);
	while(~gc(a))
	{
		n=strlen(a+1);id=0;
		b[0]='&';b[cnt=1]='#';
		rep(1,n,i)b[++cnt]=a[i],b[++cnt]='#';
		manacher();
		for(int i=2,j=0;i<=cnt;++i)
		{
			int ww=p[i]-1;
			if(ww)
			{
				if(i&1)t[++id]=(wy){j-ww/2+1,j+ww/2};
				else ++j,t[++id]=(wy){j-ww/2,j+ww/2};
			}
		}
		sort(t+1,t+1+id);
		int L=0,flag=1,ans=-1;
		while(L<n)
		{
			int mx=0;
			while(t[flag].x<=L+1&&flag<=id)mx=max(t[flag].y,mx),++flag;
			L=mx;++ans;
		}
		put(ans);
	}
	return 0;
}
posted @ 2020-04-05 16:29  chdy  阅读(129)  评论(0编辑  收藏  举报