BZOJ3790 : 神奇项链
Manacher求出所有极长回文子串后,得到一堆线段,转化成线段覆盖问题
预处理出g[i]表示左端点不超过i的右端点的最大值
贪心地线段覆盖即可
时间复杂度$O(n)$
#include<cstdio> #include<cstring> #include<algorithm> #define N 100010 using namespace std; char a[N],s[N];int n,m,f[N],i,j,r,p,ans,g[N],x,y; int main(){ while(~scanf("%s",a+1)){ n=strlen(a+1); for(r=j=ans=0,i=1;i<=n;i++)s[i<<1]=a[i],s[i<<1|1]='#',g[i]=0; s[0]='$',s[1]='#',s[m=(n+1)<<1]='@'; for(i=1;i<m;i++){ for(f[i]=r>i?min(r-i,f[p*2-i]):1;s[i-f[i]]==s[i+f[i]];f[i]++); x=(i-f[i])/2+1,y=(i+f[i])/2-1; if(x<=y)g[x]=max(g[x],y); if(i+f[i]>r)r=i+f[i],p=i; } for(i=2;i<=n;i++)g[i]=max(g[i],g[i-1]); for(j=g[1];j<n;j=g[j+1])ans++; printf("%d\n",ans); } return 0; }