BZOJ 3230: 相似子串(字尾陣列)
阿新 • • 發佈:2018-12-27
解題思路
其實題目挺好想的。首先子串排名可以由字尾陣列求得,因為不算重複的,所以後綴陣列的每個字尾排名的去掉\(lcp\)的字首排名為當前字尾的子串排名。這樣就可以預處理出每個字尾的\(l,r\),查詢的時候二分出來屬於哪個字尾,用\(rmq\)求個\(lcp\)。倒過來處理的式子比較麻煩,要先將排名轉化成位置,然後找到對應的倒過來的位置,最後在轉化為排名,具體看程式碼。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int MAXN = 100005; typedef long long LL; inline LL rd(){ LL x=0,f=1;char ch=getchar(); while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return f?x:-x; } int n,q,m; LL l[MAXN],r[MAXN]; struct SA{ int x[MAXN<<1],y[MAXN<<1],c[MAXN],sa[MAXN],rk[MAXN]; int num,height[MAXN],Min[MAXN][20]; char s[MAXN]; inline void get_SA(){m='z'; for(int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++; for(int i=2;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i;i--) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1){num=0; for(int i=n-k+1;i<=n;i++) y[++num]=i; for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) c[x[i]]++; for(int i=2;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0; swap(x,y);x[sa[1]]=1;num=1; for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num; m=num;if(n==m) break; } } inline void get_height(){ for(int i=1;i<=n;i++) rk[sa[i]]=i;int k=0,j; for(int i=1;i<=n;i++){ if(rk[i]==1) continue; if(k) k--;j=sa[rk[i]-1]; while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++; height[rk[i]]=k; } } inline void build(){ for(int i=1;i<=n;i++) Min[i][0]=height[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]); } inline int query(int x,int y){ if(x==y) return n+1; if(x>y) swap(x,y);x++;int t=log2(y-x+1); return min(Min[x][t],Min[y-(1<<t)+1][t]); } inline void prework(){ get_SA();get_height();build(); } }A,B; inline int check(LL lim){ int L=1,R=n,mid; while(L<=R){ mid=(L+R)>>1; if(l[mid]<=lim && r[mid]>=lim) return mid; if(l[mid]>lim) R=mid-1; else L=mid+1; } } int main(){ int posA,posB,lenA,lenB,L,R;LL x,y; n=rd(),q=rd();scanf("%s",A.s+1); for(int i=1;i<=n;i++) B.s[n-i+1]=A.s[i]; A.prework();B.prework(); for(int i=1;i<=n;i++) l[i]=r[i-1]+1,r[i]=l[i]+n-A.sa[i]-A.height[i]; // for(int i=1;i<=n;i++) cout<<l[i]<<" "<<r[i]<<endl; while(q--){ x=rd(),y=rd(); if(x>r[n] || y>r[n]) {puts("-1");continue;} posA=check(x);posB=check(y); // cout<<posA<<" "<<posB<<endl; lenA=A.height[posA]+x-l[posA]+1; lenB=A.height[posB]+y-l[posB]+1; // cout<<lenA<<" "<<lenB<<endl; L=min(min(lenA,lenB),A.query(posA,posB)); R=min(min(lenA,lenB),B.query(B.rk[n-(A.sa[posA]+lenA-1)+1],B.rk[n-(A.sa[posB]+lenB-1)+1])); // cout<<L<<" "<<R<<endl; printf("%lld\n",(LL)L*L+(LL)R*R); } return 0; }