首页 > 代码库 > 后缀数组之hihocoder 重复旋律1-4

后缀数组之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;
}

  

后缀数组之hihocoder 重复旋律1-4