BZOJ 3230: 相似子串(后缀数组)

传送门

解题思路

  其实题目挺好想的。首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名。这样就可以预处理出每个后缀的\(l,r\),查询的时候二分出来属于哪个后缀,用\(rmq\)求个\(lcp\)。倒过来处理的式子比较麻烦,要先将排名转化成位置,然后找到对应的倒过来的位置,最后在转化为排名,具体看代码。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int MAXN = 100005;
typedef long long LL;

inline LL rd(){
    LL x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}   

int n,q,m;
LL l[MAXN],r[MAXN];

struct SA{
    int x[MAXN<<1],y[MAXN<<1],c[MAXN],sa[MAXN],rk[MAXN];
    int num,height[MAXN],Min[MAXN][20];
    char s[MAXN];
    inline void get_SA(){m='z';
        for(int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
        for(int i=2;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){num=0;
            for(int i=n-k+1;i<=n;i++) y[++num]=i;
            for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
            memset(c,0,sizeof(c));
            for(int i=1;i<=n;i++) c[x[i]]++;
            for(int i=2;i<=m;i++) c[i]+=c[i-1];
            for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);x[sa[1]]=1;num=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])?num:++num;
            m=num;if(n==m) break; 
        }
    }
    inline void get_height(){
        for(int i=1;i<=n;i++) rk[sa[i]]=i;int k=0,j;
        for(int i=1;i<=n;i++){
            if(rk[i]==1) continue;
            if(k) k--;j=sa[rk[i]-1];
            while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++;
            height[rk[i]]=k;
        }
    }
    inline void build(){
        for(int i=1;i<=n;i++) Min[i][0]=height[i];
        for(int j=1;(1<<j)<=n;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
    }
    inline int query(int x,int y){
        if(x==y) return n+1;
        if(x>y) swap(x,y);x++;int t=log2(y-x+1);
        return min(Min[x][t],Min[y-(1<<t)+1][t]); 
    }
    inline void prework(){
        get_SA();get_height();build();
    }
}A,B;

inline int check(LL lim){
    int L=1,R=n,mid;
    while(L<=R){
        mid=(L+R)>>1;
        if(l[mid]<=lim && r[mid]>=lim) return mid;
        if(l[mid]>lim) R=mid-1;
        else L=mid+1;
    }
}

int main(){
    int posA,posB,lenA,lenB,L,R;LL x,y;
    n=rd(),q=rd();scanf("%s",A.s+1);
    for(int i=1;i<=n;i++) B.s[n-i+1]=A.s[i];
    A.prework();B.prework();
    for(int i=1;i<=n;i++)
        l[i]=r[i-1]+1,r[i]=l[i]+n-A.sa[i]-A.height[i];
//  for(int i=1;i<=n;i++) cout<<l[i]<<" "<<r[i]<<endl;
    while(q--){
        x=rd(),y=rd();
        if(x>r[n] || y>r[n]) {puts("-1");continue;}
        posA=check(x);posB=check(y);
//      cout<<posA<<" "<<posB<<endl;
        lenA=A.height[posA]+x-l[posA]+1;
        lenB=A.height[posB]+y-l[posB]+1;
//      cout<<lenA<<" "<<lenB<<endl;
        L=min(min(lenA,lenB),A.query(posA,posB));
        R=min(min(lenA,lenB),B.query(B.rk[n-(A.sa[posA]+lenA-1)+1],B.rk[n-(A.sa[posB]+lenB-1)+1]));
//      cout<<L<<" "<<R<<endl;
        printf("%lld\n",(LL)L*L+(LL)R*R);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sdfzsyq/p/10185161.html