BZOJ 3790 神奇项链 Hash+二分+树状数组

题目大意:给定一个串,问这个串最少可以由回文串拼接多少次而成(拼接可以重叠)

首先将每两个字符之间插入占位符,然后Hash+二分搞出所有极大回文串(可以用manacher,我不会)

问题转化成了给定一些区间,求最少的能覆盖整个数轴的区间

将所有区间按照某一端点排序 然后上树状数组即可

回头还是去学学manacher吧。。。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
#define BASE 131
using namespace std;
typedef unsigned long long ll;
struct Interval{
	int x,y;
	bool operator < (const Interval &Y) const
	{
		return x < Y.x ;
	}
}intervals[M];
int n;
char s[M],_s[M<<1];
ll sum1[M<<1],sum2[M<<1],power[M<<1];
namespace BIT{
	int c[M];
	inline void Update(int x,int y)
	{
		for(;x;x-=x&-x)
			c[x]=min(c[x],y);
	}
	inline int Get_Ans(int x)
	{
		int re=0x3f3f3f3f;
		for(;x<=n;x+=x&-x)
			re=min(re,c[x]);
		return re;
	}
}
void Initialize()
{
	memset(_s,0,sizeof _s);
	memset(sum1,0,sizeof sum1);
	memset(sum2,0,sizeof sum2);
	memset(BIT::c,0x3f,sizeof BIT::c);
}
bool Judge(int x,int len)
{
	int l=x-len+1;
	int r=x+len-1;
	return  sum1[r]-sum1[l-1]*power[r-l+1]==
			sum2[l]-sum2[r+1]*power[r-l+1];
}
int Bisection(int x)
{
	int l=1,r=min(x,n-x+1);
	while(l+1<r)
	{
		int mid=l+r>>1;
		if( Judge(x,mid) )
			l=mid;
		else
			r=mid;
	}
	if( Judge(x,r) )
		return r;
	return l;
}
int main()
{
	int i;
	for(i=1,power[0]=1;i<M<<1;i++)
		power[i]=power[i-1]*BASE;
	while(~scanf("%s",s+1) )
	{
		Initialize();
		n=strlen(s+1);
		for(i=1;i<=n;i++)
		{
			_s[i<<1]=s[i];
			_s[i+i-1]='#';
		}
		_s[n=n+n+1]='#';
		for(i=1;i<=n;i++)
			sum1[i]=sum1[i-1]*BASE+_s[i];
		for(i=n;i;i--)
			sum2[i]=sum2[i+1]*BASE+_s[i];
		for(i=1;i<=n;i++)
		{
			int temp=Bisection(i);
			intervals[i].x=i-temp+1;
			intervals[i].y=i+temp-1;
		}
		sort(intervals+1,intervals+n+1);
		BIT::Update(1,-1);
		for(i=1;i<=n;i++)
		{
			int temp=BIT::Get_Ans(intervals[i].x);
			BIT::Update(intervals[i].y,temp+1);
		}
		printf("%d\n", BIT::Get_Ans(n) );
	}
}


你可能感兴趣的:(hash,树状数组,二分,bzoj,BZOJ3790)