BZOJ 3230 相似子串 ——后缀数组
题目的Source好有趣。
我们求出SA,然后求出每一个后缀中与前面本质不同的字符串的个数。
然后二分求出当前的字符串。
然后就是正反两次后缀数组求LCP的裸题了。
要注意,这时两个串的起点可能会相同,所以需要判掉。
无论读入还是输出都有可能爆long long,要注意
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define maxn 200500 #define inf 0x3f3f3f3f #define ll long long int _log2[maxn]; struct Suffix_Array{ int s[maxn],cnt[maxn],tmp[maxn],sa[maxn],h[maxn],rk[maxn]; int st[maxn][21]; ll pre[maxn],Sum; void build(int n,int m) { n++; int i,j,k; F(i,0,2*n+5) sa[i]=tmp[i]=h[i]=rk[i]=0; F(i,0,m-1) cnt[i]=0; F(i,0,n-1) cnt[rk[i]=s[i]]++; F(i,1,m-1) cnt[i]+=cnt[i-1]; F(i,0,n-1) sa[--cnt[rk[i]]]=i; for (k=1;k<=n;k<<=1) { F(i,0,n-1) {j=sa[i]-k;if(j<0)j+=n;tmp[cnt[rk[j]]++]=j;} sa[tmp[cnt[0]=0]]=j=0; F(i,1,n-1) { if (rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k]) cnt[++j]=i; sa[tmp[i]]=j; } memcpy(rk,sa,n*sizeof(int));memcpy(sa,tmp,n*sizeof(int)); if (j>=n-1) break; } for (i=k=0;i<n;h[rk[i++]]=k) for (k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++); F(i,0,n-1) st[i][0]=h[i]; F(i,1,20) F(j,0,n-(1<<i)) st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]); F(i,1,n-1) pre[i]=pre[i-1]+n-sa[i]-h[i]-1; Sum=pre[n-1]; } int query(int l,int r) { int t=_log2[r-l+1]; return min(st[l][t],st[r-(1<<t)+1][t]); } ll lcp(int a,int b) { if (a==b) return inf; int aa=rk[a],bb=rk[b]; return query(min(aa,bb)+1,max(aa,bb)); } void find(ll x,int n,int &l,int &r) { int le=1,ri=n; while (le<ri) { int mid=le+ri>>1; if (pre[mid]>=x) ri=mid; else le=mid+1; } int tmp=x-pre[le-1]+h[le]; l=sa[le];r=l+tmp-1; } }SA,SB; int n,q; char s[maxn]; int main() { F(i,2,maxn-1) _log2[i]=_log2[i>>1]+1; scanf("%d%d",&n,&q); scanf("%s",s); F(i,0,n-1) { SA.s[i]=s[i]-'a'+1; SB.s[n-i-1]=s[i]-'a'+1; } SA.s[n]=0; SB.s[n]=0; SA.build(n,30); SB.build(n,30); F(i,1,q) { ll x,y; int xl,xr,yl,yr; scanf("%lld%lld",&x,&y); if (x>SA.Sum||y>SA.Sum) {printf("-1\n");continue;} SA.find(x,n,xl,xr); SA.find(y,n,yl,yr); ll lcp=SA.lcp(xl,yl),rcp=SB.lcp(n-yr-1,n-xr-1); int l=min(xr-xl+1,yr-yl+1); lcp=min(lcp,(ll)l); rcp=min(rcp,(ll)l); printf("%lld\n",lcp*lcp+rcp*rcp); } }