BZOJ3230: 相似子串
(权限题。。
做出正反后缀数组。。二分找出询问子串所在的后缀。。再加上rmq。。。
(说起来很简单但是细节还是很多的。。
比如说怎么二分找出询问子串所在的后缀呢。可以先找出每个后缀在所有子串中的排名,设这个排名为sub[i],有sub[i]=sub[i-1]+n-sa[i]+1-height[i](n-sa[i]+1就可以获得这个后缀的长度,然后再减掉与上一个后缀的重复部分)
答案的最小值要算上这两个子串的长度。子串的长度怎么算呢。比如说这个子串排名为k,所在的后缀为i,len=height[i]+k-sub[i-1](重复的部分加上比上一个串多的部分)
那它在逆串的位置?rev=n-(sa[i]+len-1)+1(就是找出这个子串最后一个字符所在的位置,然后逆过来)
(上面的其实都非常显然啦。。。你萌就别吐槽蒟蒻的智商了TAT。。。
(代码很丑很慢。。。因为蒟蒻并不会基数排序TAT。。。
#include<cstring> #include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<queue> #define rep(i,l,r) for (int i=l;i<=r;i++) #define down(i,l,r) for (int i=l;i>=r;i--) #define clr(x,y) memset(x,y,sizeof(x)) #define maxn 200500 #define inf int(1e9) #define ll long long #define mm 1000000007 #define eps 1e-9 #define low(x) x&(-x) using namespace std; char ch[maxn]; int s[maxn]; ll sub[maxn]; int n,q; ll read(){ ll x=0,f=1; char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)){x=x*10+ch-'0'; ch=getchar();} return x*f; } struct SA{ struct node{int x,y,sa; bool operator <(const node &o) const{ return x<o.x||(x==o.x&&y<o.y); } }a[maxn]; int rank[maxn],sa[maxn],h[maxn],height[maxn]; ll f[maxn][21]; void build(){ rep(i,1,n) rank[i]=s[i]; int p=1; while (p<=n){ rep(i,1,n) a[i].x=rank[i],a[i].y=rank[i+p],a[i].sa=i; sort(a+1,a+1+n); int sum=1; rank[a[1].sa]=1; rep(i,2,n) { if (a[i].x!=a[i-1].x||a[i].y!=a[i-1].y) sum++; rank[a[i].sa]=sum; } if (sum==n) break; p*=2; } rep(i,1,n) sa[i]=a[i].sa; int j=1,k=0; rep(i,1,n){ if (sa[1]==i) {h[i]=0; j=0;} else { if (j) j--; h[i]=j; k=0; while (i+j+k<=n&&sa[rank[i]-1]+j+k<=n){ if (s[i+j+k]==s[sa[rank[i]-1]+j+k]) h[i]++; else break; k++; } j=h[i]; } } rep(i,1,n) height[i]=h[sa[i]]; } void init(){ int b=int(log2(n))+1; rep(i,1,n) f[i][0]=height[i]; rep(i,1,b) rep(j,1,n) f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]); } ll rmq(int l,int r){ if (l>r) return inf; int k=0; while (1<<(k+1)<=r-l+1) k++; return min(f[l][k],f[r-(1<<k)+1][k]); } }A,B; void get(ll val,int &i,ll &len,ll &rev){ i=lower_bound(sub+1,sub+1+n,val)-sub; len=A.height[i]+val-sub[i-1]; rev=n-(A.sa[i]+len-1)+1; } int main(){ n=read(); q=read(); scanf("%s",ch+1); if (!n) {while (q--) puts("-1"); return 0;} rep(i,1,n) s[i]=ch[i]-'a'+1; A.build(); A.init(); rep(i,1,n) s[i]=ch[n-i+1]-'a'+1; B.build(); B.init(); rep(i,1,n) sub[i]=sub[i-1]+n-A.sa[i]+1-A.height[i]; ll rev0,rev1,a,b,len0,len1,l,r; int i,j; while (q--){ ll x=read(),y=read(); if (x>y) swap(x,y); if (x>sub[n]||y>sub[n]) {puts("-1"); continue;} get(x,i,len0,rev0); get(y,j,len1,rev1); a=min(min(len0,len1),A.rmq(i+1,j)); l=min(B.rank[rev0],B.rank[rev1]); r=max(B.rank[rev0],B.rank[rev1]); b=min(min(len0,len1),B.rmq(l+1,r)); printf("%lld\n",1LL*a*a+1LL*b*b); } return 0; }