SPOJ REPEATS Repeats

给定一个长为 \(n\) 的只含 ab 的字符串 \(s\),求最大整数 \(k\) 使得存在一个子串 \(s[l:r]\) 可以由某个串 \(t\) 自己拼接 \(k\) 次得到。

\(1\le n\le 50000\),有 \(T\le 20\) 组数据

题解:

SA 做法

枚举 \(l=|t|\),在字符串上取关键点 \(1,l+1,2l+1,\cdots\)。当 \(k>1\) 时,可以取相邻两个关键点所对应的 \(\operatorname{lcp}\) 以及 \(\operatorname{lcs}\) 计算答案。

SAM 做法

即找到 \(i<j\) 使得 \(\operatorname{lcp}(i,j)/(j-i)\) 最大。注意到 \(s[i:i+\operatorname{lcp}(i,j)-1]\)\(s[j:j+\operatorname{lcp}(i,j)-1]\) 是相同子串,考虑建出 SAM,只要求每个结点 \(\operatorname{right}\) 集合里最近的两个位置。

#include<bits/stdc++.h>
const int N=50003,K=16;
int n,ansk,lg[N];
char a[N],b[N];
struct suffix_array{
	int sa[N],rk[N],b[N],t[N+N],h[K][N],k;
	char*a;
	void Build(char*sc){
		for(int i=1;i<=n*2;i++)t[i]=0;
		a=sc;
		for(int i=1;i<=n;i++)sa[i]=i;
		std::sort(sa+1,sa+1+n,[&](const int&i,const int&j){return a[i]<a[j];});
		for(int i=1;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(i==1||a[sa[i]]!=a[sa[i-1]]);
		for(int j=0;(1<<j)<n;j++){
			k=0;
			for(int i=n-(1<<j)+1;i<=n;i++)t[++k]=i;
			for(int i=1;i<=n;i++)if(sa[i]>(1<<j))t[++k]=sa[i]-(1<<j);
			for(int i=1;i<=n;i++)b[i]=0;
			for(int i=1;i<=n;i++)++b[rk[i]];
			for(int i=1;i<=n;i++)b[i]+=b[i-1];
			for(int i=n;i;i--)sa[b[rk[t[i]]]--]=t[i];
			std::copy(rk+1,rk+1+n,t+1);
			for(int i=1;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(i==1||t[sa[i]]!=t[sa[i-1]]||t[sa[i]+(1<<j)]!=t[sa[i-1]+(1<<j)]);
		}
		for(int i=1,j=0;i<=n;i++){
			if(j)--j;
			for(;a[i+j]==a[sa[rk[i]+1]+j];j++);
			h[0][rk[i]]=j;
		}
		for(int j=1;j<K;j++)
			for(int i=1;i+(1<<j)<=n;i++)
				h[j][i]=std::min(h[j-1][i],h[j-1][i+(1<<j-1)]);
	}
	inline int Lcp(int i,int j){
		if(i>n||j>n)return 0;
		i=rk[i],j=rk[j];
		if(i>j)std::swap(i,j);
		int t=lg[j-i];
		return std::min(h[t][i],h[t][j-(1<<t)]);
	}
}ta,tb;
inline int Lcp(int i,int j){return ta.Lcp(  i  ,  j  );}
inline int Lcs(int i,int j){return tb.Lcp(n-i+1,n-j+1);}
int main(){
	int T;scanf("%d",&T);for(;T--;){
	scanf("%d",&n);
	for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++)scanf("%s",a+i),b[n-i+1]=a[i];
	a[n+1]=b[n+1]=0;
	ta.Build(a),tb.Build(b);
	ansk=1;
	for(int l=1,k,lcp,lcs;l<=n;l++){
		for(int i=1;i+l<=n;i+=l){
			lcp=Lcp(i,i+l),lcs=Lcs(i-1,i+l-1);
			k=(lcp+lcs)/l+1;
			if(k>ansk)ansk=k;
		}
	}
	printf("%d\n",ansk);
	}return 0;
}

posted on 2022-01-12 10:48  Dreamunk  阅读(57)  评论(0编辑  收藏  举报

导航