3230: 相似子串
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 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
ababa
3 5
5 9
8 10
Sample Output
18
16
-1
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
题解:后缀数组+二分+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;
}