后缀数组之hihocoder 重复旋律1-4
蒟蒻知道今天才会打后缀数组,而且还是nlogn^2的。。。但基本上还是跑得过的;
重复旋律1:
二分答案,把height划分集合,height<mid就重新划分,这样保证了每个集合中的LCP>=mid,套路板子题
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=1000050; int gi(){ int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int sa[N],n,len,y[N],rk,rnk[N],height[N],ans,a[N],k; struct data{ int fir,sec,id; }x[N]; bool cmp(const data &a,const data &b){ if(a.fir==b.fir) return a.sec<b.sec; else return a.fir<b.fir; } void work2(){ rk=1;y[x[1].id]=rk; for(int i=2;i<=len;i++){ if(x[i-1].fir!=x[i].fir||x[i-1].sec!=x[i].sec) rk++; y[x[i].id]=rk; } } void work(){ sort(x+1,x+1+len,cmp);work2(); for(int i=1;i<=len;i<<=1){ for(int j=1;j+i<=len;j++) x[j].fir=y[j],x[j].sec=y[j+i],x[j].id=j; for(int j=len-i+1;j<=len;j++) x[j].fir=y[j],x[j].sec=0,x[j].id=j; sort(x+1,x+1+len,cmp);work2(); if(rk==len) break; } } void get_height(){ int kk=0;for(int i=1;i<=len;i++) rnk[sa[i]]=i; for(int i=1;i<=len;i++){ if(kk) kk--; int j=sa[rnk[i]-1]; while(a[i+kk]==a[j+kk]) kk++; height[rnk[i]]=kk; } } bool check(int mid){ int ret=1,size=1; for(int i=2;i<=len;i++){ if(height[i]<mid) ret=max(ret,size),size=1; else size++; } return ret>=k; } int main(){ len=gi();k=gi();for(int i=1;i<=len;i++) a[i]=gi(); for(int i=1;i<=len;i++) x[i].id=i,x[i].fir=x[i].sec=a[i]; work();for(int i=1;i<=len;i++) sa[y[i]]=i; get_height();int l=0,r=len; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); return 0; }
重复旋律2:
和上题差不多,二分答案,把height划分集合,维护集合中的最左端和最右端,判断两端相减>=mid;
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=1000050; int gi(){ int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int sa[N],n,len,y[N],rk,rnk[N],height[N],ans,a[N],k; struct data{ int fir,sec,id; }x[N]; bool cmp(const data &a,const data &b){ if(a.fir==b.fir) return a.sec<b.sec; else return a.fir<b.fir; } void work2(){ rk=1;y[x[1].id]=rk; for(int i=2;i<=len;i++){ if(x[i-1].fir!=x[i].fir||x[i-1].sec!=x[i].sec) rk++; y[x[i].id]=rk; } } void work(){ sort(x+1,x+1+len,cmp);work2(); for(int i=1;i<=len;i<<=1){ for(int j=1;j+i<=len;j++) x[j].fir=y[j],x[j].sec=y[j+i],x[j].id=j; for(int j=len-i+1;j<=len;j++) x[j].fir=y[j],x[j].sec=0,x[j].id=j; sort(x+1,x+1+len,cmp);work2(); if(rk==len) break; } } void get_height(){ int kk=0;for(int i=1;i<=len;i++) rnk[sa[i]]=i; for(int i=1;i<=len;i++){ if(kk) kk--; int j=sa[rnk[i]-1]; while(a[i+kk]==a[j+kk]) kk++; height[rnk[i]]=kk; } } bool check(int mid){ int minn=sa[1],maxn=sa[1]; for(int i=2;i<=len;i++){ if(height[i]<mid){ if(maxn-minn>=mid) return 1; minn=maxn=sa[i]; } minn=min(minn,sa[i]),maxn=max(maxn,sa[i]); } return 0; } int main(){ len=gi();for(int i=1;i<=len;i++) a[i]=gi(); for(int i=1;i<=len;i++) x[i].id=i,x[i].fir=x[i].sec=a[i]; work();for(int i=1;i<=len;i++) sa[y[i]]=i; get_height();int l=0,r=len; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); return 0; }
重复旋律3:
还是板子,把两个串用"#"分开,然后还是二分答案划分height集合
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=1000050; int gi(){ int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int sa[N],n,len,y[N],rk,rnk[N],height[N],ans,cor[N],num[3]; char ch[N],ch2[N],a[N]; struct data{ int fir,sec,id; }x[N]; bool cmp(const data &a,const data &b){ if(a.fir==b.fir) return a.sec<b.sec; else return a.fir<b.fir; } void work2(){ rk=1;y[x[1].id]=rk; for(int i=2;i<=len;i++){ if(x[i-1].fir!=x[i].fir||x[i-1].sec!=x[i].sec) rk++; y[x[i].id]=rk; } } void work(){ sort(x+1,x+1+len,cmp);work2(); for(int i=1;i<=len;i<<=1){ for(int j=1;j+i<=len;j++) x[j].fir=y[j],x[j].sec=y[j+i],x[j].id=j; for(int j=len-i+1;j<=len;j++) x[j].fir=y[j],x[j].sec=0,x[j].id=j; sort(x+1,x+1+len,cmp);work2(); if(rk==len) break; } } void get_height(){ int kk=0;for(int i=1;i<=len;i++) rnk[sa[i]]=i; for(int i=1;i<=len;i++){ if(kk) kk--; int j=sa[rnk[i]-1]; while(a[i+kk]==a[j+kk]) kk++; height[rnk[i]]=kk; } } bool check(int mid){ memset(num,0,sizeof(num));num[cor[sa[1]]]++; for(int i=2;i<=len;i++){ if(height[i]<mid){ if(num[1]&&num[2]) return 1; memset(num,0,sizeof(num)); } num[cor[sa[i]]]++; } return 0; } int main(){ scanf("%s",ch+1);scanf("%s",ch2+1); int len1=strlen(ch+1),len2=strlen(ch2+1);len=len1+len2+1; for(int i=1;i<=len1;i++) a[i]=ch[i],cor[i]=1;a[len1+1]='#'; for(int i=1;i<=len2;i++) a[len1+1+i]=ch2[i],cor[len1+1+i]=2; for(int i=1;i<=len;i++) x[i].id=i,x[i].fir=x[i].sec=a[i]-'a'+1; work();for(int i=1;i<=len;i++) sa[y[i]]=i; get_height(); int l=0,r=min(len1,len2); while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); return 0; }
重复旋律4:
真正的大火题。。。
首先暴力需要枚举长度L,以及串开始的位置i,则以i开始,循环长度为L的答案为1+lcp(i,i+L)/L,记为k(i,L);这个把图画一下还是很好理解的
优化的话可以只考虑L的整数倍上的位置,然后对于不在整数倍上的x,在他后面的最近的L的整数倍数为p,则k(x,L)不大于k(p,L)+1,
因为我们知道i和i+L的lcp,那么i+lcp和i+L+lcp是失配的,不然lcp还可以更长,所以在这里就gi了。
然后因为x和p相差不超过L,所以k的值顶多加1;
然后假设存在一个x使得k(x,L)==k(p,L)+1,那么x=i-L+lcp%L,这相当把失配位置往前挪(续..)了一个L,可以画图理解一下,所以只需要额外判断k(x,L)即可
然后
for(int L=1;L<=len;L++){
for(int i=L;i+L<=len;i+=L){
}
}
是一个经典的nlogn复杂度,很优秀。。。lcp的话就是height的rmq最小值即可,ST表即可。妙不可言
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=200050; int gi(){ int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int sa[N],len,y[N],rk,rnk[N],height[N],ans,pre[N],pre2[N],ST[N][20]; char ch[N],ch2[N],a[N]; struct data{ int fir,sec,id; }x[N]; bool cmp(const data &a,const data &b){ if(a.fir==b.fir) return a.sec<b.sec; else return a.fir<b.fir; } void work2(){ rk=1;y[x[1].id]=rk; for(int i=2;i<=len;i++){ if(x[i-1].fir!=x[i].fir||x[i-1].sec!=x[i].sec) rk++; y[x[i].id]=rk; } } void work(){ sort(x+1,x+1+len,cmp);work2(); for(int i=1;i<=len;i<<=1){ for(int j=1;j+i<=len;j++) x[j].fir=y[j],x[j].sec=y[j+i],x[j].id=j; for(int j=len-i+1;j<=len;j++) x[j].fir=y[j],x[j].sec=0,x[j].id=j; sort(x+1,x+1+len,cmp);work2(); if(rk==len) break; } } void get_height(){ int kk=0;for(int i=1;i<=len;i++) rnk[sa[i]]=i; for(int i=1;i<=len;i++){ if(kk) kk--; int j=sa[rnk[i]-1]; while(a[i+kk]==a[j+kk]) kk++; height[rnk[i]]=kk; } } void make_ST(){ pre[0]=1;for(int i=1;i<=16;i++) pre[i]=pre[i-1]<<1; pre2[0]=-1;for(int i=1;i<=len;i++) pre2[i]=pre2[i>>1]+1; for(int i=2;i<=len;i++) ST[i][0]=height[i]; for(int j=1;j<=16;j++) for(int i=2;i<=len;i++){ if(i+pre[j]-1<=len){ ST[i][j]=min(ST[i][j-1],ST[i+pre[j-1]][j-1]); } } } int query(int l,int r){ int x=pre2[r-l+1]; return min(ST[l][x],ST[r-pre[x]+1][x]); } int LCP(int l,int r){ if(l>r) swap(l,r); return query(l+1,r); } int main(){ scanf("%s",a+1);len=strlen(a+1); for(int i=1;i<=len;i++) x[i].id=i,x[i].fir=x[i].sec=a[i]-'a'+1; work();for(int i=1;i<=len;i++) sa[y[i]]=i; get_height();make_ST(); for(int L=1;L<=len;L++){ for(int i=L;i+L<=len;i+=L){ int lcp=LCP(rnk[i],rnk[i+L]);ans=max(ans,1+lcp/L); ans=max(ans,LCP(rnk[i-L+lcp%L],rnk[i+lcp%L])/L+1); } } printf("%d\n",ans); return 0; }