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;

}

  

 

你可能感兴趣的:(ZOJ)