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;
}

  

 

posted @ 2014-12-11 12:55  Claris  阅读(306)  评论(0编辑  收藏  举报