=== ===
这里放传送门
=== ===
题解
这道题实际上就是给定子串的排名然后求它们的LCP和LCS。所以首先需要对正串和反串分别建立后缀数组,求出height。因为要计算LCP和LCS就要知道它们在正串和反串中所处的位置,所以就考虑如何根据给定的排名求这个东西。在后缀数组里求不相同子串的时候就是按照后缀的排名,每次用这个后缀的前缀数量减去和前一个后缀重复的前缀数量也就是它的height然后再累加到答案里。那也就是说按照字典序考察每个后缀的时候,它贡献的子串也是按照字典序排列的。那么可以考虑二分,把每个后缀贡献的子串求出来存在数组里再做一个前缀和然后在这个数组里二分。这样就可以得到排名为k的子串是由哪一个后缀贡献的,接着就可以得到它在这个后缀里的位置然后就可以得到它原本的长度。在用height数组求LCP和LCS的时候对子串长度取一下Min就没问题了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Pow 18
#define clear(x)(memset(x,0,sizeof(x)));
using namespace std;
const long long inf=1e18;
int len,q,Xx[100010],Yy[100010],b[100010],m,p,*x,*y,lg[100010];
long long v[100010];
char s[100010];
bool 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]);
}
struct SuffixArray{
int SA[100010],rank[100010],height[100010],Min[100010][20];
void get(){
clear(b);x=Xx;y=Yy;m=200;
for (int i=0;i<len;i++) ++b[x[i]=s[i]];
for (int i=1;i<=m;i++) b[i]+=b[i-1];
for (int i=len-1;i>=0;i--) SA[--b[x[i]]]=i;
for (int k=1;k<=len;k<<=1){
p=0;
for (int i=len-k;i<len;i++) y[p++]=i;
for (int i=0;i<len;i++)
if (SA[i]>=k) y[p++]=SA[i]-k;
for (int i=0;i<=m;i++) b[i]=0;
for (int i=0;i<len;i++) ++b[x[i]];
for (int i=1;i<=m;i++) b[i]+=b[i-1];
for (int i=len-1;i>=0;i--) SA[--b[x[y[i]]]]=y[i];
swap(x,y);x[SA[0]]=0;p=1;
for (int i=1;i<len;i++)
x[SA[i]]=cmp(SA[i-1],SA[i],k)?p-1:p++;
if (p>=len) break;m=p;
}
p=0;
for (int i=0;i<len;i++) rank[SA[i]]=i;
for (int i=0;i<len;i++){
if (rank[i]==0) continue;
int j=SA[rank[i]-1];
while (i+p<len&&j+p<len&&s[i+p]==s[j+p]) ++p;
height[rank[i]]=p;
if (p!=0) --p;
}
for (int i=0;i<len;i++) Min[i][0]=height[i];
for (int i=1;i<=Pow;i++)
for (int j=0;j<len;j++){
int pos=j+(1<<(i-1));
if (pos>=len) continue;
Min[j][i]=min(Min[j][i-1],Min[pos][i-1]);
}
}
int ask(int l,int r,int limit){
int ans,j;
l=rank[l];r=rank[r];
if (l==r) return limit;
if (l>r) swap(l,r);
l++;j=lg[r-l+1];
ans=min(Min[l][j],Min[r-(1<<j)+1][j]);
ans=min(ans,limit);
return ans;
}
}P,N;
void getstr(int len){
char c;
for (int i=0;i<len;i++){
c=getchar();
while (c<'a'||c>'z') c=getchar();
s[i]=c;
}
}
void reverse(){
for (int i=0;i+i<len;i++)
swap(s[i],s[len-i-1]);
}
int find(int l,int r,long long x,int &L){
int mid;
long long res=x;
while (l!=r){
mid=(l+r)>>1;
if (v[mid]>=x) r=mid;
else l=mid+1;
}
if (l==len) return -1;
if (l!=0) res-=v[l-1];
L=res+P.height[l];//计算当前子串的长度
return P.SA[l];
}
int main()
{
scanf("%d%d",&len,&q);
for (int i=1,p=0;i<=len;i++){
while ((1<<p)<=i) ++p;
lg[i]=p-1;
}
getstr(len);P.get();
reverse();N.get();
for (int i=0;i<len;i++) v[i]=len-P.SA[i]-P.height[i];
for (int i=1;i<len;i++) v[i]+=v[i-1];
v[len]=inf;//便于判断无解
for (int i=1;i<=q;i++){
int xp,yp,xl,yl,limit;
long long x,y,LCP,LCS;
scanf("%I64d%I64d",&x,&y);
xp=find(0,len,x,xl);
yp=find(0,len,y,yl);
if (xp==-1||yp==-1){
printf("-1\n");continue;
}
limit=min(xl,yl);//注意LCP和LCS的最长长度不能超过子串长度
LCP=P.ask(xp,yp,limit);
xp=xp+xl-1;yp=yp+yl-1;
xp=len-xp-1;yp=len-yp-1;//求出当前这个串在反串中的位置
LCS=N.ask(xp,yp,limit);
LCP*=LCP;LCS*=LCS;
printf("%I64d\n",LCP+LCS);
}
return 0;
}