Manacher 贪心
题目大意: 给你一个字符串,你可以造任意的回文串并把它们拼起来得到原串,求最少拼接次数(回文串可重叠)。
先Manacher求出 p p p数组,把每个点看成一个一条线段,左右端点分别为 i − p [ i ] i-p[i] i−p[i]和 i + p [ i ] i+p[i] i+p[i]。然后就变成线段覆盖问题(用最少的线段覆盖住整个区间),贪心即可(我懒就用了堆)。
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
struct node{ int l,r; };
int id,r,ans,p[N]; char s[N],s1[N];
F bool operator < (node a,node b){ return a.l>b.l; }
priority_queue <node> q;
int main(){
while (scanf("%s",s)!=EOF){
memset(s1,0,sizeof(s1)),s1[0]='$';
for (int i=0;s[i];i++) s1[2*i+1]='#',s1[2*i+2]=s[i];
s1[strlen(s1)]='#',memset(p,0,sizeof(p)),id=r=ans=0;
for (int i=1;s1[i];i++){
p[i]=r>i?min(p[id*2-i],r-i):1;
while (s1[i+p[i]]==s1[i-p[i]]) p[i]++;
if (p[i]+i>r) r=p[i]+i,id=i;
}
while (!q.empty()) q.pop(); int len=strlen(s1)-1;
for (int i=2;i<len;i++) q.push((node){i-p[i]+1,i+p[i]-1});
for (int lst=1,now=0;lst<len;ans++,lst=now)
while (!q.empty()&&q.top().l<=lst) now=max(now,q.top().r),q.pop();
printf("%d\n",ans-1);
}
return 0;
}