【bzoj3790】神奇项链——manacher+贪心
听说这道题可以用树状数组什么鬼的做,反正我不会,还是老老实实打manacher+贪心大法吧……
我们只要在跑manacher的过程中,用一个结构体(也可以直接用数组)来记录以每个字符为对称轴的最长回文的最左端和最右端,然后就得到了一些线段,于是问题完美地转换成了求取最少段的线段来完全覆盖一个区间了,直接排完序后贪心一遍即可。当然这里面有些比较是不必要的,但我也懒得去优化了。
具体实现细节看代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; char s[100010]; int p[100010],pos,cnt; struct point { int begin,end; }a[100010]; bool cmp(point b,point c) { return b.begin<c.begin||(b.begin==c.begin&&b.end>c.end); } int main() { while(~scanf("%s",s)) { memset(p,0,sizeof(p)); int len=strlen(s); pos=0;cnt=0; for(int i=len;i>=0;i--) { s[i*2+2]=s[i]; s[i*2+1]='#'; } s[0]='$'; for(int i=2;i<len*2+1;i++) { if(pos+p[pos]>i)p[i]=min(p[pos*2-i],pos+p[pos]-i); else p[i]=1; while(s[i+p[i]]==s[i-p[i]])p[i]++; if(pos+p[pos]<i+p[i])pos=i; a[++cnt].begin=i-p[i]+1;a[cnt].end=i+p[i]-1; } sort(a+1,a+1+cnt,cmp); int ans=0,r=a[1].end,now=2; while(r<len*2) { int r0=r; while(now<=cnt&&a[now].begin<=r) { r0=max(r0,a[now].end); now++; } ans++;r=r0; } printf("%d\n",ans); } return 0; }