[后缀数组] BZOJ4650: [Noi2016] 优秀的拆分

来源:互联网 发布:崩坏学园漫画淘宝 编辑:程序博客网 时间:2024/05/22 07:44

fi 表示以 i 结尾的AA类型字符串的个数, gi 则表示以 i 为开头的

那么答案就是 fi×gi+1

枚举A的长度 L,每隔 L 个位置放一个关键点,那么A肯定经过一个关键点,用SA找一下就好了

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>using namespace std;typedef long long ll;const int N=100010;int t,n;char a[N];int t1[N],t2[N],c[N],lg2[N];struct SufArr{  int sa[N],height[N],rank[N];  int st[N][20];  void build(){    int *x=t1,*y=t2,m=26;    for(int i=0;i<=m;i++) c[i]=0;    for(int i=1;i<=n;i++) c[x[i]=a[i]-'a'+1]++;    for(int i=1;i<=m;i++) c[i]+=c[i-1];    for(int i=n;i;i--) sa[c[x[i]]--]=i;    for(int k=1;k<=n;k<<=1){      int p=0;      for(int i=n-k+1;i<=n;i++) y[++p]=i;      for(int i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;      for(int i=0;i<=m;i++) c[i]=0;      for(int i=1;i<=n;i++) c[x[y[i]]]++;      for(int i=1;i<=m;i++) c[i]+=c[i-1];      for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i];      swap(x,y); p=1; x[sa[1]]=1;      for(int i=2;i<=n;i++)    x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?p:++p;      if(p>=n) break;      m=p;    }  }  void GetHeight(){    int k=0,j;    for(int i=1;i<=n;i++) rank[sa[i]]=i;    for(int i=1;i<=n;height[rank[i++]]=k)      for(k?k--:0,j=sa[rank[i]-1];a[i+k]==a[j+k];k++);  }  void Pre(){    int t=lg2[n];    for(int i=1;i<=n;i++) st[i][0]=height[i];    for(int k=1;k<=t;k++)      for(int i=1;i+(1<<k)-1<=n;i++)    st[i][k]=min(st[i][k-1],st[i+(1<<k-1)][k-1]);  }  int Query(int x,int y){    if(x==y) return n-x+1;    int l=rank[x],r=rank[y];    if(l>r) swap(l,r); l++;    int t=lg2[r-l+1];    return min(st[l][t],st[r-(1<<t)+1][t]);  }}S,P;int f[N],g[N]; int main(){  freopen("1.in","r",stdin);  freopen("1.out","w",stdout);  scanf("%d",&t);  for(int i=1;i<=30000;i++) lg2[i]=lg2[i-1]+((1<<lg2[i-1]+1)==i);  while(t--){    scanf("%s",a+1); n=strlen(a+1); t1[n+1]=t2[n+1]=0;    S.build(); S.GetHeight(); S.Pre();    reverse(a+1,a+1+n);    P.build(); P.GetHeight(); P.Pre();    for(int i=1;i<=n;i++) f[i]=g[i]=0;    for(int L=1;L+L<=n;L++)      for(int lst=1,i=L+1;i<=n;lst=i,i+=L){    int left=min(P.Query(n-lst+1,n-i+1),L),right=min(S.Query(lst,i),L),d=left+right-L;    if(d<0) continue;    f[i+right-d]++; f[i+right]--;    g[lst-left+d+1]--; g[lst-left+1]++;      }    for(int i=1;i<=n;i++) f[i]+=f[i-1],g[i]+=g[i-1];    ll ans=0;    for(int i=1;i<n;i++) ans+=1LL*f[i]*g[i+1];    printf("%lld\n",ans);  }  return 0;}
原创粉丝点击