poj 3693 Maximum_repetition_substring

题目链接
题解:
找循环节个数最多的子串并且字典序最小。
先从简单入手,不考虑子串字典序。
暴力做法是(O(n^2))枚举子串,用MP的fail数组求出其最小周期长度,用Len/T即循环节个数,这里注意必须整除才能更新答案。复杂度是(O(n^2))的。
假设一个子串循环节个数为k,且每个周期的长度是T,那么该子串的长度为kT,且至少包含k个是T的整倍数的下标。比如s[2,7]=ababab是一个T=2,k=3的子串,里面包含了1T,2T,3T这三个下标。
枚举T,再枚举i,使得下标iT和(i+1)T合法,求一下(a=lcp(iT,(i+1)T))(b=lcs(iT,(i+1)T)),如果a+b>T证明此时一定有一个子串的循环节个数大于1,且包含iT和(i+1)T。可以知道这个子串L最小到iT-b+1,R最大到(i+1)T+a-1,[L,R]内包含多少个T,就是当前最大值,用p=(R-L+1)/T更新答案即可。
如果还需要考虑字典序,我们就是在[L,R-p*T+1]之中找一个Rank最小的,那个就是包含iT,(i+1)T的最小字典序的且循环节个数最大的子串。
所以需要用后缀数组预处理Height,Rank数组和反串的Height数组,构建这三个数组的ST表,这样就可以O(1)得知答案了。枚举T再枚举i的复杂度是(O(nln n)),查询的复杂度是常数较大的(O(1))。总复杂度为(O(nln n))
PS:细节较多,看代码更适合理解。

code

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define fcl fclose(stdin); fclose(stdout); return 0
int n;
char str[200010];
int C[200010],Ws[200010],Rank[200010],Sa[200010],Height[200010],ST[100010][19];
int Rank2[200010],Sa2[200010],Height2[200010],ST2[100010][19];
int Table[100010],st[100010][19];
void da(){
    //构建原串的Height数组和Rank数组
	int i,j,c,k,m=200,p;
	memset(Ws,0,(m+1)<<2);
	for(i=0;i<n;++i) Ws[Rank[i]=str[i]]++;
	for(i=1;i<=m;++i) Ws[i]+=Ws[i-1];
	for(i=0;i<n;++i) Sa[--Ws[Rank[i]]]=i;
	for(c=1;c<=n;c<<=1,m=p){
		for(j=0,i=n-c;i<n;++i) C[j++]=i;
		for(i=0;i<n;++i) if(Sa[i]>=c) C[j++]=Sa[i]-c;
		memset(Ws,0,(m+1)<<2);
		for(i=0;i<n;++i) Ws[Rank[i]]++;
		for(i=1;i<=m;++i) Ws[i]+=Ws[i-1];
		for(i=n-1;i>=0;--i) Sa[--Ws[Rank[C[i]]]]=C[i];
		for(C[Sa[0]]=0,i=1,p=0;i<n;++i)
			C[Sa[i]]=(Rank[Sa[i]]==Rank[Sa[i-1]]&&Rank[Sa[i]+c]==Rank[Sa[i-1]+c]?p:++p);
		memcpy(Rank,C,n<<2);
	}
	for(i=0,j=0,k=0;i<n;++i){
		if(Rank[i]==0) continue;
		for(k=(k?k-1:0),j=Sa[Rank[i]-1];str[i+k]==str[j+k];++k);
		Height[Rank[i]]=k;
	}
	for(i=n-1;i>=0;--i){
		ST[i][0]=Height[i];
		for(j=0;j<18;++j){
			if(i+(1<<j)<n) ST[i][j+1]=min(ST[i][j],ST[i+(1<<j)][j]);
			else ST[i][j+1]=ST[i][j];
		}
		st[i][0]=i;
		for(int j=0;j<18;++j){
			if(i+(1<<j)<n) st[i][j+1]=(Rank[st[i][j]]<Rank[st[i+(1<<j)][j]]?st[i][j]:st[i+(1<<j)][j]);
			else st[i][j+1]=st[i][j];
		}
	}
}
void da2(){
    //只不过是为了构建反串的Height而已,除了数组名字外其他的几乎同上
	int i,j,c,k,m=200,p;
	memset(Ws,0,(m+1)<<2);
	for(i=0;i<n;++i) Ws[Rank2[i]=str[i]]++;
	for(i=1;i<=m;++i) Ws[i]+=Ws[i-1];
	for(i=0;i<n;++i) Sa2[--Ws[Rank2[i]]]=i;
	for(c=1;c<=n;c<<=1,m=p){
		for(j=0,i=n-c;i<n;++i) C[j++]=i;
		for(i=0;i<n;++i) if(Sa2[i]>=c) C[j++]=Sa2[i]-c;
		memset(Ws,0,(m+1)<<2);
		for(i=0;i<n;++i) Ws[Rank2[i]]++;
		for(i=1;i<=m;++i) Ws[i]+=Ws[i-1];
		for(i=n-1;i>=0;--i) Sa2[--Ws[Rank2[C[i]]]]=C[i];
		for(C[Sa2[0]]=0,i=1,p=0;i<n;++i)
			C[Sa2[i]]=(Rank2[Sa2[i]]==Rank2[Sa2[i-1]]&&Rank2[Sa2[i]+c]==Rank2[Sa2[i-1]+c]?p:++p);
		memcpy(Rank2,C,n<<2);
	}
	for(i=0,j=0,k=0;i<n;++i){
		if(Rank2[i]==0) continue;
		for(k=(k?k-1:0),j=Sa2[Rank2[i]-1];str[i+k]==str[j+k];++k);
		Height2[Rank2[i]]=k;
	}
	for(i=n-1;i>=0;--i){
		ST2[i][0]=Height2[i];
		for(j=0;j<18;++j){
			if(i+(1<<j)<n) ST2[i][j+1]=min(ST2[i][j],ST2[i+(1<<j)][j]);
			else ST2[i][j+1]=ST2[i][j];
		}
	}
}
void Da(){
	da();
	reverse(str,str+n-1);
	da2();
	reverse(str,str+n-1);
}
inline int Lcp(int a,int b){
	a=Rank[a]; b=Rank[b];
	if(a>b) swap(a,b);
	a++;
	return min(ST[a][Table[b-a+1]],ST[b-(1<<Table[b-a+1])+1][Table[b-a+1]]);
}
inline int Lcs(int a,int b){
	a=n-2-a, b=n-2-b;
	a=Rank2[a]; b=Rank2[b];
	if(a>b) swap(a,b);
	a++;
	return min(ST2[a][Table[b-a+1]],ST2[b-(1<<Table[b-a+1])+1][Table[b-a+1]]);
}
int QueryMin(int l,int r){
    //查询[l,r]内Rank最小的下标
	int t=Table[r-l+1];
	return Rank[st[l][t]]<Rank[st[r-(1<<t)+1][t]]?st[l][t]:st[r-(1<<t)+1][t];
}
bool Judge(int s1,int t1,int s2,int t2){
    //当答案相同时比较字典序
	int z=Lcp(s1,s2);
	for(int i=s1+z,j=s2+z;i<=t1&&j<=t2;++i,++j)
	    //其实这个for循环最多执行一次,因为已经减去了LCP,str[i]一定不等于str[j]。
		if(str[i]!=str[j]) return str[i]<str[j];
	return t1-s1<t2-s2;
}
int main(){
	for(int i=1,k=0;i<=100000;i<<=1,++k){
		for(int j=i;j<(i<<1)&&j<=100000;++j) Table[j]=k;
	}
	int pp=0;
	while(scanf("%s",str),str[0]!='#'){
	    ++pp; printf("Case %d: ",pp);
		n=strlen(str);
		int l,i,x,y,z,ans=1,fr,to,p,q,minn=0x7f7f7f7f;
		for(int i=0;i<n;++i) if(str[i]<minn) minn=str[i],fr=i,to=i;
		str[n++]='$'; str[n]=0;
		Da();
		for(l=1;(l<<1)<=n;++l){
			for(i=0;i+l<n;i+=l*(y/l+1)){
				x=i+l;
				y=Lcp(i,x); z=Lcs(i,x);
				if(y+z<l) continue;
				p=y+z+l-1;
				p/=l;
				q=QueryMin(i-z+1,x+y-p*l);
				if(p>ans) ans=p,fr=q,to=q+p*l-1;
				else if(p==ans&&Judge(q,q+p*l-1,fr,to))
				    fr=q,to=q+p*l-1;
			}
		}
		for(int i=fr;i<=to;++i) putchar(str[i]);
		puts("");
	}
	fcl;
}

原文地址:https://www.cnblogs.com/kito/p/7111414.html