[后缀数组 枚举 字符串分段] BZOJ 4650 [Noi2016]优秀的拆分

先求出所有的双倍字串 然后再乱来

我们枚举一半的长度 然后把整个字符串按照长度 l分段 

起点分别为 s[1] s[l+1] s[2l+1]  ... 分段后记作 S1 S2 S3 .....

那么双倍的串一定会包含其中两个 我们求 Si Si-1的LCS  Si Si+1的LCP

如果LCS+LCP>=l 那么存在 双倍串 记录一下 这里我用了差分


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++; 
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s){
  char c=nc(); int len=0;
  for (;!(c>='a' && c<='z');c=nc());
  for (;c>='a' && c<='z';s[++len]=c,c=nc()); s[++len]=0; return len-1;
}

const int N=30005;

struct SArray{
  int n;  
  int sa[N],t1[N],t2[N],c[N];  
  int height[N],rank[N];  
  inline void SA(char *r,int m){  
    int *x=t1,*y=t2;  
    for (int i=0;i<=m;i++) c[i]=0;  
    for (int i=1;i<=n;i++) c[x[i]=r[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);  
      x[sa[1]]=1; p=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;  
    }  
  }  
  inline void GetHeight(char *S,int *sa,int n){  
    int j,k=0;  
    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];S[i+k]==S[j+k];)  
	k++;  
  }
  int st[N][20],Log[N];  
  inline void Pre(int n,int *a){  
    for (int i=2;i<=n;i++) Log[i]=Log[i>>1]+1;  
    for (int i=1;i<=n;i++)  
      st[i][0]=a[i];  
    for (int k=1;k<20;k++)  
      for (int i=1;i<=n;i++){  
	st[i][k]=st[i][k-1];  
	if (i+(1<<(k-1))<=n)  
	  st[i][k]=min(st[i][k],st[i+(1<<(k-1))][k-1]);  
      }  
  }  
  inline int Query(int l,int r){
    if (l>r) swap(l,r);
    int t=Log[r-l+1];  
    return min(st[l][t],st[r-(1<<t)+1][t]);  
  }
  inline void Build(char *S,int len){
    cl(sa); cl(height); cl(rank); cl(t1); cl(t2);
    n=len; SA(S,30);
    GetHeight(S,sa,n);
    Pre(len,height);
  }
  inline int LCP(int x,int y){
    if (x>n || x<=0 || y>n || y<=0) return 0;
    if (rank[x]>rank[y]) swap(x,y);
    return Query(rank[x]+1,rank[y]);
  }
}suf,pre;

struct BIT{
  int c[N],maxn;
  void init(int n){
    maxn=n; for (int i=1;i<=maxn;i++) c[i]=0;
  }
  void add(int s,int t,int r){
    if (s>t) return;
    c[s]+=r; if (t+1<=maxn) c[t+1]-=r;
  }
  void calc(){
    for (int i=2;i<=maxn;i++)
      c[i]+=c[i-1];
  }
}lft,rgt;

char str[N];
int n;
ll Ans;

int main(){
  int T,lcp,lcs;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(T);
  while (T--){
    n=read(str);
    lft.init(n); rgt.init(n);
    suf.Build(str,n);
    reverse(str+1,str+n+1);
    pre.Build(str,n);
    reverse(str+1,str+n+1);
    for (int i=1;i<=(n>>1);i++){
      for (int j=1;j<=n;j+=i){
	lcp=min(i,suf.LCP(j,j+i));
	lcs=min(i,pre.LCP(n-(j-1)+1,n-(j+i-1)+1));
	if (lcp+lcs>=i){
	  rgt.add(j-lcs,min(j-lcs+(lcp+lcs-i),j-1),1);
	  lft.add(max(j+i-1+lcp-(lcp+lcs-i),j+i),j+i-1+lcp,1);
	}
      }
    }
    lft.calc(); rgt.calc();
    Ans=0;
    for (int i=1;i<n;i++)
      Ans+=(ll)lft.c[i]*rgt.c[i+1];
    printf("%lld\n",Ans);
  }
  return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值