题目大意:给出一个字符串,求出这是最少由多少个回文串组成的。回文串可以重叠。
思路:将原串中的所有回文串都统计出来,然后变成一些区间,问题就转化成了区间并的问题。
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;
}