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

来源:互联网 发布:河东区中山门淘宝街 编辑:程序博客网 时间:2024/06/06 17:02

题目
分析,这道题很显然要找两个相同的字串,也就是两个后缀公共前缀,很自然地可以想到,可以使用后缀数组。
所谓的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 17int 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);    }}
0 0
原创粉丝点击