bzoj 3230 相似子串 —— 后缀数组+二分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230
先算出每个后缀贡献子串的区间;
然后前缀LCP直接查询,后缀LCP二分长度,查询即可;
注意本质不同的子串的个数是 long long 级别!!
于是读入有 long long —— 快读也要写成 long long 的!!!
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=1e5+5; int n,m,sa[xn],rk[xn],tax[xn],tp[xn],ht[xn][20],bin[20],bit[xn]; ll pl[xn],pr[xn],cnt;//ll! char s[xn]; ll rd()//ll { ll ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void Rsort() { for(int i=1;i<=m;i++)tax[i]=0; for(int i=1;i<=n;i++)tax[rk[tp[i]]]++; for(int i=1;i<=m;i++)tax[i]+=tax[i-1]; for(int i=n;i;i--)sa[tax[rk[tp[i]]]--]=tp[i]; } void work() { for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i; Rsort(); for(int k=1;k<=n;k<<=1) { int num=0; for(int i=n-k+1;i<=n;i++)tp[++num]=i; for(int i=1;i<=n;i++) if(sa[i]>k)tp[++num]=sa[i]-k; Rsort(); swap(rk,tp); rk[sa[1]]=1; num=1; for(int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num; if(num==n)break; m=num; } } void get() { int k=0; for(int i=1;i<=n;i++) { if(rk[i]==1)continue; if(k)k--; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++; ht[rk[i]][0]=k; } bin[0]=1; for(int i=1;i<20;i++)bin[i]=(bin[i-1]<<1); bit[1]=0; for(int i=2;i<=xn;i++)bit[i]=bit[i>>1]+1; for(int j=1;j<20;j++) for(int i=1;i<=n&&i+bin[j]-1<=n;i++) ht[i][j]=min(ht[i][j-1],ht[i+bin[j-1]][j-1]); } int getlcp(int x,int y) { if(x==y)return n-x+1; x=rk[x]; y=rk[y]; if(x>y)swap(x,y); x++; int w=bit[y-x+1]; return min(ht[x][w],ht[y-bin[w]+1][w]); } void init() { for(int i=1;i<=n;i++) { ll num=n-sa[i]+1-ht[i][0]; pl[i]=cnt+1; cnt+=num; pr[i]=cnt; } } void find(ll x,int &sl,int &sr) { int l=1,r=n,res; while(l<=r) { int mid=((l+r)>>1); if(x>=pl[mid]&&x<=pr[mid]){res=mid; sl=sa[mid]; break;}//mid:rk if(x<pl[mid])r=mid-1; else l=mid+1; } int st=sl+ht[res][0]; sr=st+(x-pl[res]+1)-1; } int ask(int x,int y) { int l=0,r=min(x,y),res;//min while(l<=r) { int mid=((l+r)>>1); if(getlcp(x-mid+1,y-mid+1)>=mid)res=mid,l=mid+1; else r=mid-1; } return res; } int main() { n=rd(); int Q=rd(); scanf("%s",s+1); m=125; work(); get(); init(); for(int i=1;i<=Q;i++) { ll x=rd(),y=rd();//ll if(x>cnt||y>cnt){puts("-1"); continue;} int l1,r1,l2,r2; find(x,l1,r1); find(y,l2,r2); int mnn=min(r1-l1+1,r2-l2+1); int a=min(getlcp(l1,l2),mnn); int b=min(ask(r1,r2),mnn); printf("%lld\n",(ll)a*a+(ll)b*b); } return 0; }