【后缀数组】[UVA10829]L-Gap substring

题目
分析,这道题很显然要找两个相同的字串,也就是两个后缀公共前缀,很自然地可以想到,可以使用后缀数组。
所谓的L-Gap字串,就是两个相同的字串,中间间隔了g个字符,所以,我们枚举这两个字串的长度l,然后看0和l,l和l*2…..分别从这两个位置向前和向后匹配,匹配的长度减去l就是这个位置对答案的贡献。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 50000*2
#define MAXC 128
#define Log 17
int array[4][MAXN+10],st[MAXN+10][Log+1],g,*sa,*nsa,*rk,*nrk,b[MAXN+10],n,na,m,T,height[MAXN+10],ans,cnt;
char s[MAXN+10];
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void cal_sa(){
    rk=array[0],nrk=array[1],sa=array[2],nsa=array[3];
    int i,k;
    rk[n]=nrk[n]=-1;
    memset(b,0,sizeof b);
    for(i=0;i<n;i++)
        b[s[i]]++;
    for(i=1;i<=MAXC;i++)
        b[i]+=b[i-1];
    for(i=0;i<n;i++)
        sa[--b[s[i]]]=i;
    for(rk[sa[0]]=0,i=1;i<n;i++){
        rk[sa[i]]=rk[sa[i-1]];
        if(s[sa[i]]!=s[sa[i-1]])
            rk[sa[i]]++;
    }
    for(k=1;rk[sa[n-1]]<n-1;k<<=1){
        for(i=0;i<n;i++)
            b[rk[sa[i]]]=i;
        for(i=n-1;i>=0;i--)
            if(sa[i]>=k)
                nsa[b[rk[sa[i]-k]]--]=sa[i]-k;
        for(i=n-k;i<n;i++)
            nsa[b[rk[i]]--]=i;
        for(nrk[nsa[0]]=0,i=1;i<n;i++){
            nrk[nsa[i]]=nrk[nsa[i-1]];
            if(rk[nsa[i]]!=rk[nsa[i-1]]||rk[nsa[i]+k]!=rk[nsa[i-1]+k])
                nrk[nsa[i]]++;
        }
        swap(sa,nsa);
        swap(rk,nrk);
    }
}
void read(){
    Read(g);
    scanf("%s",s);
    na=strlen(s);
    s[na]='$';
    int i;
    for(i=1;i<=na;i++)
        s[na+i]=s[na-i];
    n=(na<<1)|1;
    s[n]=0;
}
void cal_height(){
    int i,j,k=0;
    for(i=0;i<n;i++)
        if(!rk[i])
            height[rk[i]]=0;
        else{
            if(k)
                k--;
            for(j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
            height[rk[i]]=k;
        }
}
void prepare(){
    int i,j;
    for(i=0;i<n;i++)
        st[i][0]=height[i];
    for(j=1;j<=Log;j++)
        for(i=0;i<n;i++)
            if(i+(1<<(j-1))<n)
                st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int Get_st(int rk1,int rk2){
    if(rk1>rk2)
        swap(rk1,rk2);
    int t=log2(rk2-rk1);
    return min(st[rk1+1][t],st[rk2-(1<<t)+1][t]);
}
void solve(){
    int i,j,k,t;
    for(i=1;i<na;i++){
        for(j=0;j<na&&j+i+g<na;j+=i){
            k=i+j+g;
            t=min(Get_st(rk[j],rk[k]),i);
            t+=min(Get_st(rk[n-j],rk[n-k]),i-1);
            ans+=max(t-i+1,0);
        }
    }
}
int main()
{
    Read(T);
    while(T--){
        memset(st,0,sizeof st);
        ans=0;
        read();
        cal_sa();
        cal_height();
        prepare();
        solve();
        printf("Case %d: %d\n",++cnt,ans);
    }
}

你可能感兴趣的:(C++,后缀数组,uva)