BZOJ 3790 神奇项链 Hash+二分

题目大意:给出一个字符串,求出这是最少由多少个回文串组成的。回文串可以重叠。


思路:将原串中的所有回文串都统计出来,然后变成一些区间,问题就转化成了区间并的问题。


CODE:


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 400010
#define BASE 1333
#define INF 0x3f3f3f3f
using namespace std;
#define min(a,b) ((a) < (b) ? (a):(b))

int length;
char _s[MAX],s[MAX];
unsigned long long hash[MAX],_hash[MAX],power[MAX];

struct Interval{
	int x,y;
	
	Interval(int _ = 0,int __ = 0):x(_),y(__) {}
	bool operator <(const Interval &a)const {
		if(x == a.x)	return y > a.y;
		return x < a.x;
	}
}block[MAX];

Interval GetMosl(int pos)
{
	int l = 1,r = min(pos,length - pos + 1),ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		unsigned long long h1 = hash[pos + mid - 1] - hash[pos - 1] * power[mid];
		unsigned long long h2 = _hash[pos - mid + 1] - _hash[pos + 1] * power[mid];
		if(h1 == h2)	ans = mid,l = mid + 1;
		else	r = mid - 1;
	}
	return Interval(pos - ans + 1,pos + ans - 1);
}

int fenwick[MAX];

inline void Fix(int x,int c)
{
	for(; x; x -= x&-x)
		fenwick[x] = min(fenwick[x],c);
}

inline int GetAns(int x)
{
	int re = INF;
	for(; x <= length; x += x&-x)
		re = min(re,fenwick[x]);
	return re;
}

int main()
{
	power[0] = 1;
	for(int i = 1; i < MAX; ++i)	power[i] = power[i - 1] * BASE;
	while(scanf("%s",_s + 1) != EOF) {
		length = strlen(_s + 1);
		s[1] = '$';
		for(int i = 1; i <= length; ++i)
			s[i << 1] = _s[i],s[i << 1|1] = '$';
		s[(length + 1) << 1] = '\0';
		length = strlen(s + 1);
		for(int i = 1; i <= length; ++i)	hash[i] = hash[i -  1] * BASE + s[i];
		_hash[length + 1] = 0;
		for(int i = length; i; --i)		_hash[i] = _hash[i + 1] * BASE + s[i];
		for(int i = 1; i <= length; ++i)
			block[i] = GetMosl(i);
		sort(block + 1,block + length + 1);
		memset(fenwick,0x3f,sizeof(fenwick));
		Fix(1,-1);
		for(int temp,i = 1; i <= length; ++i) {
			temp = GetAns(block[i].x);
			Fix(block[i].y,temp + 1);
		}
		printf("%d\n",GetAns(length));
	}
	return 0;
}




你可能感兴趣的:(hash,区间合并,bzoj,hash+二分)