蒟蒻知道今天才会打后缀数组,而且还是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
",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
",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
",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
",ans);
return 0;
}