bzoj 3230 相似子串——后缀数组
Posted narh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 3230 相似子串——后缀数组相关的知识,希望对你有一定的参考价值。
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230
作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好序了,所以直接在 sa[ ] 上二分就能找到询问的子串。
找最长公共前缀就用 ht[ ] 和子串的长度比较就行。找最长公共后缀就一开始把原串翻转,做出翻转后的 ht[ ] ,就能查询了。
也可以二分一个最长公共后缀的位置,然后用正常的 ht[ ] 判断。
注意 long long 。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e5+5,K=20; int n,sa[2][N],rk[2][N],tp[N],tx[N];ll sm[N]; int ht[2][N][K],bin[K],lg[N]; char s[N]; struct Node{int ps,len;}; int Mn(int a,int b){return a<b?a:b;} int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar(); return fx?ret:-ret; } ll rdl() { ll ret=0;bool fx=1;char ch=getchar(); while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar(); return fx?ret:-ret; } void init() { lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1; bin[0]=1;for(int i=1;i<=lg[n];i++)bin[i]=bin[i-1]<<1; } void Rsort(int n,int nm,int x) { for(int i=1;i<=nm;i++)tx[i]=0; for(int i=1;i<=n;i++)tx[rk[x][i]]++; for(int i=2;i<=nm;i++)tx[i]+=tx[i-1]; for(int i=n;i;i--)sa[x][tx[rk[x][tp[i]]]--]=tp[i]; } void get_sa(int n,int x) { int nm=30; for(int i=1;i<=n;i++)tp[i]=i,rk[x][i]=s[i]-‘a‘+1; Rsort(n,nm,x); for(int k=1;k<=n;k<<=1) { int tot=0; for(int i=n-k+1;i<=n;i++)tp[++tot]=i; for(int i=1;i<=n;i++) if(sa[x][i]>k)tp[++tot]=sa[x][i]-k; Rsort(n,nm,x);memcpy(tp,rk[x],sizeof rk[x]); nm=1;rk[x][sa[x][1]]=1; for(int i=2,u,v;i<=n;i++) { u=sa[x][i]+k;v=sa[x][i-1]+k;if(u>n)u=0;if(v>n)v=0; rk[x][sa[x][i]]=(tp[sa[x][i]]==tp[sa[x][i-1]]&&tp[u]==tp[v])?nm:++nm; } if(nm==n)break; } } void get_ht(int n,int x) { for(int i=1,k=0,j;i<=n;i++) { for(k?k--:0,j=sa[x][rk[x][i]-1];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++); ht[x][rk[x][i]][0]=k; } for(int j=1;j<=lg[n];j++) for(int i=1;i+bin[j]-1<=n;i++) ht[x][i][j]=Mn(ht[x][i][j-1],ht[x][i+bin[j-1]][j-1]); } int get_lcp(int l,int r,int x) { if(l==r)return n-l+1; l=rk[x][l]; r=rk[x][r]; if(l>r)swap(l,r); int d=lg[r-l]; return Mn(ht[x][l+1][d],ht[x][r-bin[d]+1][d]); } Node fnd(ll x) { int l=1,r=n,ret=0; while(l<=r) { int mid=l+r>>1; if(sm[mid]>=x)ret=mid,r=mid-1; else l=mid+1; } if(!ret)return (Node){0,0}; int d=n-sa[0][ret]+1-ht[0][ret][0]; d=ht[0][ret][0]+(x-sm[ret-1]); return (Node){sa[0][ret],d}; } int main() { n=rdn();int Q=rdn();scanf("%s",s+1);init(); get_sa(n,0);get_ht(n,0); reverse(s+1,s+n+1);get_sa(n,1);get_ht(n,1); for(int i=1;i<=n;i++) sm[i]=sm[i-1]+(n-sa[0][i]+1)-ht[0][i][0]; ll x,y; Node a,b;//long long!!! while(Q--) { x=rdl(); y=rdl();//long long!! a=fnd(x); b=fnd(y); if(!a.ps||!b.ps){puts("-1");continue;} x=Mn(get_lcp(a.ps,b.ps,0),Mn(a.len,b.len)); y=Mn(get_lcp(n-(a.ps+a.len-1)+1,n-(b.ps+b.len-1)+1,1),Mn(a.len,b.len)); printf("%lld ",x*x+y*y); } return 0; }
以上是关于bzoj 3230 相似子串——后缀数组的主要内容,如果未能解决你的问题,请参考以下文章