bzoj 3230: 相似子串 (后缀数组+RMQ+二分)

3230: 相似子串

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 1502   Solved: 364
[ Submit][ Status][ Discuss]

Description

Input

输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。

Output

输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。

Sample Input

5 3
ababa
3 5
5 9
8 10

Sample Output

18
16
-1

HINT

样例解释

第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。

第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。

第3组询问:不存在第10个子串。输出-1。


数据范围

N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成

Source

[ Submit][ Status][ Discuss]

题解:后缀数组+二分+RMQ

因为是本质不同的,所以我们直接可以得到每个位置为止的子串的个数,然后可以二分答案,求出排名为k的串在哪个后缀中,并且在后缀中的长度。

知道排名,然后RMQ求解即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100003
#define M 20
#define LL long long 
using namespace std;
int n,m,len,q,p,st[20][N],l[N];
int sa[N],rank[N],height[N],xx[N],yy[N],*x,*y,a[N],b[N];
int sa1[N],rank1[N],height1[N],st1[20][N];
LL sum[N];
char s[N];
struct data{
    int pos,x;
};
int cmp(int i,int j,int l)
{
    return y[i]==y[j]&&(i+l>len?-1:y[i+l])==(j+l>len?-1:y[j+l]);
}
void get_SA(int sa[N],int rank[N],int height[N],int st[M][N]){
    m=200; x=xx; y=yy;
    memset(b,0,sizeof(b));
    for (int i=1;i<=len;i++) b[x[i]=a[i]]++;
    for (int i=1;i<=m;i++) b[i]+=b[i-1];
    for (int i=len;i>=1;i--) sa[b[x[i]]--]=i;
    for (int k=1;k<=len;k<<=1) {
        p=0;
        for (int i=len-k+1;i<=len;i++) y[++p]=i;
        for (int i=1;i<=len;i++)
         if (sa[i]>k) y[++p]=sa[i]-k;
        for (int i=1;i<=m;i++) b[i]=0;
        for (int i=1;i<=len;i++) b[x[y[i]]]++;
        for (int i=1;i<=m;i++) b[i]+=b[i-1];
        for (int i=len;i>=1;i--) sa[b[x[y[i]]]--]=y[i];
        swap(x,y); p=2; x[sa[1]]=1;
        for (int i=2;i<=len;i++) 
         x[sa[i]]=cmp(sa[i-1],sa[i],k)?p-1:p++;
        if (p>len) break;
        m=p+1;
    }
    p=0;
    for (int i=1;i<=len;i++) rank[sa[i]]=i;
    for (int i=1;i<=len;i++) {
        if (rank[i]==1) continue;
        int j=sa[rank[i]-1];
        while (i+p<=len&&j+p<=len&&a[i+p]==a[j+p]) p++;
        height[rank[i]]=p;
        p=max(p-1,0);
    }
    for (int i=1;i<=len;i++) st[0][i]=height[i];
    for (int i=1;i<=17;i++)
     for (int j=1;j+(1<<i)-1<=len;j++)
      st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
    int j=0;
    for (int i=1;i<=len;i++){
        if ((1<<(j+1))<=i) j++;
        l[i]=j;
    }   
}
data solve(LL x)
{
    int l=1; int r=len; int ans=0;
    while (l<=r) {
        int mid=(l+r)/2;
        if(sum[mid]>=x&&sum[mid-1]<x) {
            ans=mid;
            break;
        }
        if (sum[mid]>x) r=mid-1;
        else l=mid+1;
    }
    LL t=x-sum[ans-1];
    data a; a.pos=ans; a.x=height[ans]+(int)t;
    return a;
}
int calc(int x,int y)
{
   if (x==y) return len;
   if (x>y) swap(x,y);
   int k=l[y-x]; x++;
   return min(st[k][x],st[k][y-(1<<k)+1]); 
}
int calc1(int x,int y)
{
    x=rank1[x]; y=rank1[y];
    if (x==y) return len;
    if (x>y) swap(x,y);
    int k=l[y-x]; x++;
    return min(st1[k][x],st1[k][y-(1<<k)+1]);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
    scanf("%d%d",&len,&q);
    scanf("%s",s+1);
    for (int i=1;i<=len;i++) a[i]=s[i]-'a'+1;
    get_SA(sa,rank,height,st);
    for (int i=1;i<=len;i++)
     sum[i]=(LL)(len-sa[i]+1-height[i]);
    for (int i=1;i<=len;i++) sum[i]+=sum[i-1];
    for (int i=1;i<=len;i++) a[len-i+1]=s[i]-'a'+1;
    get_SA(sa1,rank1,height1,st1);
    for (int i=1;i<=q;i++) {
        LL x,y; scanf("%I64d%I64d",&x,&y);
        if(x>sum[len]||y>sum[len]) {
            printf("-1\n");
            continue;
        }
        data ansl=solve(x);
        data ansr=solve(y);
        int ll=sa[ansl.pos]+ansl.x-1; 
        int rr=sa[ansr.pos]+ansr.x-1; 
        int a=min(min(ansl.x,ansr.x),calc(ansl.pos,ansr.pos));
        int b=min(min(ansl.x,ansr.x),calc1(len-ll+1,len-rr+1));
        LL ans=(LL)a*(LL)a+(LL)b*(LL)b;
        printf("%lld\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值