BZOJ3230: 相似子串

(权限题。。

BZOJ3230: 相似子串_第1张图片

做出正反后缀数组。。二分找出询问子串所在的后缀。。再加上rmq。。。

(说起来很简单但是细节还是很多的。。

比如说怎么二分找出询问子串所在的后缀呢。可以先找出每个后缀在所有子串中的排名,设这个排名为sub[i],有sub[i]=sub[i-1]+n-sa[i]+1-height[i](n-sa[i]+1就可以获得这个后缀的长度,然后再减掉与上一个后缀的重复部分)

答案的最小值要算上这两个子串的长度。子串的长度怎么算呢。比如说这个子串排名为k,所在的后缀为i,len=height[i]+k-sub[i-1](重复的部分加上比上一个串多的部分)

那它在逆串的位置?rev=n-(sa[i]+len-1)+1(就是找出这个子串最后一个字符所在的位置,然后逆过来)

(上面的其实都非常显然啦。。。你萌就别吐槽蒟蒻的智商了TAT。。。

(代码很丑很慢。。。因为蒟蒻并不会基数排序TAT。。。

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define maxn 200500
#define inf int(1e9)
#define ll long long
#define mm 1000000007
#define eps 1e-9
#define low(x) x&(-x)
using namespace std;
char ch[maxn];
int s[maxn];
ll sub[maxn];
int n,q;
ll read(){
    ll x=0,f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct SA{
    struct node{int x,y,sa;
        bool operator <(const node &o) const{
            return x<o.x||(x==o.x&&y<o.y);
        }
    }a[maxn];
    int rank[maxn],sa[maxn],h[maxn],height[maxn];
    ll f[maxn][21];
    void build(){
        rep(i,1,n) rank[i]=s[i];
        int p=1;
        while (p<=n){
            rep(i,1,n) a[i].x=rank[i],a[i].y=rank[i+p],a[i].sa=i;
            sort(a+1,a+1+n);
            int sum=1; rank[a[1].sa]=1;
            rep(i,2,n) {
                if (a[i].x!=a[i-1].x||a[i].y!=a[i-1].y) sum++;
                rank[a[i].sa]=sum;
            }
            if (sum==n) break;
            p*=2;
        }
        rep(i,1,n) sa[i]=a[i].sa;
        int j=1,k=0;
        rep(i,1,n){
            if (sa[1]==i) {h[i]=0; j=0;}
            else {
                if (j) j--;
                h[i]=j; k=0;
                while (i+j+k<=n&&sa[rank[i]-1]+j+k<=n){
                    if (s[i+j+k]==s[sa[rank[i]-1]+j+k]) h[i]++; else break;
                    k++;
                } 
                j=h[i];
            }
        }
        rep(i,1,n) height[i]=h[sa[i]];
    }
    void init(){
        int b=int(log2(n))+1;
        rep(i,1,n) f[i][0]=height[i];
        rep(i,1,b) rep(j,1,n) f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
    }
    ll rmq(int l,int r){
        if (l>r) return inf;
        int k=0;
        while (1<<(k+1)<=r-l+1) k++;
        return min(f[l][k],f[r-(1<<k)+1][k]);
    }
}A,B;
void get(ll val,int &i,ll &len,ll &rev){
    i=lower_bound(sub+1,sub+1+n,val)-sub;
    len=A.height[i]+val-sub[i-1];
    rev=n-(A.sa[i]+len-1)+1;
}
int main(){
    n=read(); q=read();
    scanf("%s",ch+1);
    if (!n) {while (q--) puts("-1"); return 0;}
    rep(i,1,n) s[i]=ch[i]-'a'+1; A.build(); A.init();
    rep(i,1,n) s[i]=ch[n-i+1]-'a'+1; B.build(); B.init();
    rep(i,1,n) sub[i]=sub[i-1]+n-A.sa[i]+1-A.height[i];
    ll rev0,rev1,a,b,len0,len1,l,r;
    int i,j;
    while (q--){
        ll x=read(),y=read();
        if (x>y) swap(x,y);
        if (x>sub[n]||y>sub[n]) {puts("-1"); continue;}
        get(x,i,len0,rev0); get(y,j,len1,rev1);
        a=min(min(len0,len1),A.rmq(i+1,j));
        l=min(B.rank[rev0],B.rank[rev1]); r=max(B.rank[rev0],B.rank[rev1]);
        b=min(min(len0,len1),B.rmq(l+1,r));
        printf("%lld\n",1LL*a*a+1LL*b*b);
    }    
    return 0;
}

 

你可能感兴趣的:(BZOJ3230: 相似子串)