Description
Input
输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。
Output
输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。
Sample Input
ababa
3 5
5 9
8 10
Sample Output
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'组成
首先把原来的串建一遍后缀数组,
接着可以求出子串数目的前缀和。
即按照rank下来,sum[i]表示前i个后缀有sum[i]个不同的子串。
这个过程很简单,因为相同的子串是height[i]个,统计即可。
然后要求出排名为x的子串,只要在sum数组里面二分即可;
利用这个方法,我们可以十分快速地求出给出的两个子串的具体位置。
既然知道了具体位置(假设分别是L1~R1,L2~R2),
那么它们的LCP其实就是:
R1-L1+1
R2-L2+1
min(height[rank[L1]+1..rank[L2]])
以上三者的最小值。应该很好理解的。
对了要注意L1=L2的情况,第三个要特判的。
为了求出第三个,我们需要额外建立一个st表以便O(1)查询。
那么最长公共后缀呢?(LCS)
很简单,我们可以把原来的串反过来,然后求一遍sa。
接着的方法就跟上面的一样了。
建议开二维数组,比如sa[opt][],opt=0时存的是正序的sa,opt=1时是逆序的。
当然struct等等都可以。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll read(){
ll x=(ll)0,f=(ll)1;char ch=getchar();
while (ch<'0' || ch>'9'){if (ch=='-') f=(ll)-1;ch=getchar();}
while (ch>='0' && ch<='9'){x=x*(ll)10+ch-'0';ch=getchar();}
return x*f;
}
const int
N=100005,
logN=20;
char s[2][N];
int n,Q,Len;
int cnta[N],cntb[N],a[N],b[N<<1],tsa[N];
int sa[2][N],rank[2][N],height[2][N];
int st[2][N][logN];
ll sumSub[2][N];
void Get_SA(int x){
for (int i=0;i<=25;i++) cnta[i]=0;
for (int i=1;i<=Len;i++) cnta[s[x][i]-97]++;
for (int i=1;i<=25;i++) cnta[i]+=cnta[i-1];
for (int i=Len;i;i--) sa[x][cnta[s[x][i]-97]--]=i;
rank[x][sa[x][1]]=1;
for (int i=2;i<=Len;i++)
rank[x][sa[x][i]]=rank[x][sa[x][i-1]]+(s[x][sa[x][i]]!=s[x][sa[x][i-1]]);
for (int j=1;rank[x][sa[x][Len]]!=Len;j<<=1){
for (int i=1;i<=Len;i++) a[i]=rank[x][i],b[i]=rank[x][i+j];
for (int i=0;i<=Len;i++) cnta[i]=cntb[i]=0;
for (int i=1;i<=Len;i++) cnta[a[i]]++,cntb[b[i]]++;
for (int i=1;i<=Len;i++) cnta[i]+=cnta[i-1],cntb[i]+=cntb[i-1];
for (int i=Len;i;i--) tsa[cntb[b[i]]--]=i;
for (int i=Len;i;i--) sa[x][cnta[a[tsa[i]]]--]=tsa[i];
rank[x][sa[x][1]]=1;
for (int i=2;i<=Len;i++)
rank[x][sa[x][i]]=rank[x][sa[x][i-1]]+
(a[sa[x][i]]!=a[sa[x][i-1]] || b[sa[x][i]]!=b[sa[x][i-1]]);
}
}
void Get_H(int x){
int len=0;
for (int i=1;i<=Len;i++){
if (len) len--;
while (s[x][i+len]==s[x][sa[x][rank[x][i]-1]+len]) len++;
height[x][rank[x][i]]=len;
}
}
void PreRMQ(int id){
for (int i=1;i<=Len;i++)
st[id][i][0]=i;
for (int j=1;j<=logN;j++)
for (int i=1;i<=Len;i++)
if (i+(1<<j)-1>Len) break;
else
if (height[id][st[id][i][j-1]]>height[id][st[id][i+(1<<(j-1))][j-1]])
st[id][i][j]=st[id][i+(1<<(j-1))][j-1];
else
st[id][i][j]=st[id][i][j-1];
}
void PreSUM(int x){
sumSub[x][0]=(ll)0;
for (int i=1;i<=Len;i++)
sumSub[x][i]=sumSub[x][i-1]+(ll)(Len-height[x][i]-sa[x][i]+1);
}
int RMQ(int id,int x,int y){
int k=(int)((double)log(y-x+1)/(double)log(2));
if (height[id][st[id][x][k]]<height[id][st[id][y-(1<<k)+1][k]])
return st[id][x][k]; else
return st[id][y-(1<<k)+1][k];
}
int BS(int x,ll y){
int L=0,R=Len,mid;
while (L<R){
mid=(L+R+1)>>1;
if (sumSub[x][mid]<=y) L=mid;
else R=mid-1;
}
return L;
}
void Get_Place(ll &l1,ll &r1){
int t1=BS(0,l1);
if (l1==sumSub[0][t1]) l1=sa[0][t1],r1=Len;
else r1=l1-sumSub[0][t1]+height[0][t1+1],
l1=sa[0][t1+1],r1+=l1-1;
}
ll Get_ANS(int x,int l,int r){
if (rank[x][l]>rank[x][r]) swap(l,r);
if (l==r) return (ll)Len-l+1;
return (ll)height[x][RMQ(x,rank[x][l]+1,rank[x][r])];
}
int main(){
n=(int)read(),Q=(int)read();
scanf("%s",s[0]+1);
Len=strlen(s[0]+1);
for (int i=1;i<=Len;i++) s[1][Len-i+1]=s[0][i];
for (int i=0;i<2;i++)
Get_SA(i),Get_H(i),PreRMQ(i),PreSUM(i);
ll l1,l2,r1,r2,len1;
while (Q--){
l1=read(),l2=read();
if (l1>sumSub[0][Len] || l2>sumSub[0][Len]){
puts("-1");continue;}
Get_Place(l1,r1),Get_Place(l2,r2);
len1=r1-l1+(ll)1,len1=min(len1,r2-l2+(ll)1);
r1=Len-r1+(ll)1,r2=Len-r2+(ll)1;
ll A=Get_ANS(0,l1,l2),B=Get_ANS(1,r1,r2);
A=min(A,len1),B=min(B,len1);
printf("%lld\n",A*A+B*B);
}
return 0;
}