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

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值