后缀数组_DC3(模板大全)

/*
suffix数组:第i位到最后的字符串
sa数组:将排序后的后缀的开头位置顺次放入SA中,称为后缀数组
rank数组:令rank[i]保存suffix[i]在排序中的名次,名次数组
*/
#include "stdio.h"
#include "string.h"
#define maxn 20004
#define maxm 1000005

#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[maxn],wb[maxn],wv[maxn],ws[maxm];
int c0(int *r,int a,int b)
{return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
int c12(int k,int *r,int a,int b)
{if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
 else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
void sort(int *r,int *a,int *b,int n,int m)
{
     int i;
     for(i=0;i<n;i++) wv[i]=r[a[i]];
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[wv[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
     return;
}
void dc3(int *r,int *sa,int n,int m)
{
     int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
     r[n]=r[n+1]=0;
     for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
     sort(r+2,wa,wb,tbc,m);
     sort(r+1,wb,wa,tbc,m);
     sort(r,wa,wb,tbc,m);
     for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
     rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
     if(p<tbc) dc3(rn,san,tbc,p);
     else for(i=0;i<tbc;i++) san[rn[i]]=i;
     for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
     if(n%3==1) wb[ta++]=n-1;
     sort(r,wb,wa,ta,m);
     for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
     for(i=0,j=0,p=0;i<ta && j<tbc;p++)
     sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
     for(;i<ta;p++) sa[p]=wa[i++];
     for(;j<tbc;p++) sa[p]=wb[j++];
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     int i,j,k=0;
     for(i=1;i<=n;i++) rank[sa[i]]=i;
     for(i=0;i<n;height[rank[i++]]=k)
     for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
     return;
}
int RMQ[maxn];
int mm[maxn];
int best[20][maxn];
void initRMQ(int n)
{
     int i,j,a,b;
     for(mm[0]=-1,i=1;i<=n;i++)
     mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
     for(i=1;i<=n;i++) best[0][i]=i;
     for(i=1;i<=mm[n];i++)
     for(j=1;j<=n+1-(1<<i);j++)
     {
       a=best[i-1][j];
       b=best[i-1][j+(1<<(i-1))];
       if(RMQ[a]<RMQ[b]) best[i][j]=a;
       else best[i][j]=b;
     }
     return;
}
int askRMQ(int a,int b)
{
    int t;
    t=mm[b-a+1];b-=(1<<t)-1;
    a=best[t][a];b=best[t][b];
    return RMQ[a]<RMQ[b]?a:b;
}
int lcp(int a,int b)//从头开始比较a和b的对应字符持续相等的最远值
{
    int t;
    a=rank[a];b=rank[b];
    if(a>b) {t=a;a=b;b=t;}
    return(height[askRMQ(a+1,b)]);
}
/*
*/
char st[maxn];
int r[maxn*3],sa[maxn*3];
/*求最长回文长
int main()
{
    int i,n,len,k,ans=0,w;
    scanf("%s",st);
    len=strlen(st);
    for(i=0;i<len;i++) r[i]=st[i];
    r[len]=1;
    for(i=0;i<len;i++) r[i+len+1]=st[len-1-i];
    n=len+len+1;
    r[n]=0;
    dc3(r,sa,n+1,128);
    calheight(r,sa,n);
    for(i=1;i<=n;i++) RMQ[i]=height[i];
    initRMQ(n);
    for(i=0;i<len;i++)
    {
      k=lcp(i,n-i);
      if(k*2>ans) ans=k*2,w=i-k;
      k=lcp(i,n-i-1);
      if(k*2-1>ans) ans=k*2-1,w=i-k+1;
    }
    st[w+ans]=0;
    printf("%s\n",st+w);
    return 0;
}*/
/*求最大公共字串长
int main()
{
    int i,j,n,ans=0;
    scanf("%s",st);
    j=strlen(st);
    st[j]=1;
    scanf("%s",st+j+1);
    n=strlen(st);
    for(i=0;i<n;i++) r[i]=st[i];
    r[n]=0;
    dc3(r,sa,n+1,128);
    calheight(r,sa,n);
    for(i=2;i<=n;i++)
    if(height[i]>ans)
    if((j<sa[i-1] && j>sa[i])
    || (j>sa[i-1] && j<sa[i])) ans=height[i];
    printf("%d\n",ans);
    return 0;
}
*/
/*不可重叠最长重复子串
 POJ 1743 最长重复子串(不可重叠)

题意:

有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:

1.长度至少为5个音符

2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。)

3.重复出现的同一主题不能有公共部分。

int check(int *sa,int n,int k)
{
    int i,max=sa[1],min=sa[1];
    for(i=2;i<=n;i++)
    {
      if(height[i]<k) max=min=sa[i];
      else
      {
        if(sa[i]<min) min=sa[i];
        if(sa[i]>max) max=sa[i];
        if(max-min>k) return(1);
      }
    }
    return(0);
}
int main()
{
    int i,j=0,k,n;
    int min,mid,max;
    scanf("%d",&n);
    while(n!=0)
    {
      n--;
      for(i=0;i<n;i++)
        scanf("%d",&r[i]);
      for(i=0;i<n;i++)
       r[i]=r[i]-r[i+1]+90;
      r[n]=0;
      dc3(r,sa,n+1,200);
      calheight(r,sa,n);
      min=1;max=n/2;
      while(min<=max)
      {
        mid=(min+max)/2;
        if(check(sa,n,mid)) min=mid+1;
        else max=mid-1;
      }
      if(max>=4) printf("%d\n",max+1);
      else printf("0\n");
      scanf("%d",&n);
    }
    return 0;
}
*/
/*
可重叠的 k 次最长重复子串
给定一个长度为n的整数序列,求其中至少出现k次的子序列长度最长为多长
int check(int n,int k,int mid)
{
    int i,s=1;
    for(i=1;i<=n;i++)
    if(height[i]>=mid)
    {
      s++;
      if(s>=k) return(1);
    }
    else s=1;
    return(0);
}
int main()
{
    int i,k,n;
    int min,mid,max;
    scanf("%d %d",&n,&k);
    for(i=0;i<n;i++)
    {
      scanf("%d",&r[i]);
      r[i]++;
    }
    r[n]=0;
    dc3(r,sa,n+1,1000002);
    calheight(r,sa,n);
    min=1;max=n;
    while(min<=max)
    {
      mid=(min+max)/2;
      if(check(n,k,mid)) min=mid+1;
      else max=mid-1;
    }
    printf("%d\n",max);
    return 0;
}
*/

/*
不相同的连续子串的个数
int main()
{
    int i,n,t,ans;
    scanf("%d",&t);
    while(t-->0)
    {
      scanf("%s",st);
      n=strlen(st);
      for(i=0;i<n;i++) r[i]=st[i];
      r[n]=0;
      dc3(r,sa,n+1,128);
      calheight(r,sa,n);
      ans=n*(n+1)/2;
      for(i=1;i<=n;i++) ans-=height[i];
      printf("%d\n",ans);
    }
    return 0;
}
*/
/*
长度不小于 k 的公共子串的个数
int f[maxn];
int a[maxn],b[maxn],c;
long long ss,ans;
int main()
{
    int i,k,l,n,t;
    scanf("%d",&k);
    while(k!=0)
    {
      scanf("%s",st);
      l=strlen(st);
      st[l]=1;
      scanf("%s",st+l+1);
      n=strlen(st);
      for(i=0;i<n;i++) r[i]=st[i];
      r[n]=0;
      dc3(r,sa,n+1,128);
      calheight(r,sa,n);
      for(i=2;i<=n;i++)
      {
        f[i]=sa[i]<l;
        height[i]-=k-1;
        if(height[i]<0) height[i]=0;
      }
      height[n+1]=0;
      a[0]=-1;ans=0;
      for(t=0;t<=1;t++)
      for(c=0,ss=0,i=2;i<=n;i++)
      {
        if(f[i]!=t) ans+=ss;
        c++;
        a[c]=height[i+1];
        b[c]=f[i]==t;
        ss+=(long long)a[c]*b[c];
        while(a[c-1]>=a[c])
        {
          ss-=(long long)(a[c-1]-a[c])*b[c-1];
          a[c-1]=a[c];
          b[c-1]+=b[c];
          c--;
        }
      }
      printf("%I64d\n",ans);
      scanf("%d",&k);
    }
    return 0;
}
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值