Loading

POJ 3693 Maximum repetition substring(后缀数组+ST表)

题意

定义一个字符串的复读数为它可以被分割成最多的的若干个相同连续的子串个数,求一个字符串中复读次数最大的子串位置,若有相同,输出字典序最小的。

\(1 \leq \text{length}(string)\leq 10^5\)

思路

后缀数组的 \(H\) 是排名相邻两个后缀的 \(\text{lcp}\) ,那么只要对这个数组处理一个 \(\text{ST}\)表,就可以 \(O(1)\) 询问任两个后缀的 \(\text{lcp}\) 了。

写法比较难想到,首先枚举分成的若干个相同连续子串的长度 \(l\), 然后把字符串分成 \(\displaystyle{n\over l}\) 份,然后对于第 \(i\) 份和第 \(i+1\) 份的开头,求一次 \(\text{lcp}\),就可以知道以第 \(i\) 份串开头的,相同连续子串的长度为 \(l\) 的最大复读数,当然答案可能不在整的位置上,求出的 \(\text{lcp}\) 可能也不是 \(l\) 的倍数,所以再往回跳到刚好的位置,然后再算一次答案即可。

求字典序最小的可以暴力一点,把复读数最大的 \(l\) 都存下来,然后按照后缀数组的顺序一一检查是否合法。

这题讲起来是很难讲清楚,建议阅读罗穗骞的论文《后缀数组——处理字符串的有力工具》。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=1e5+5;
int sa[N],rk[N],H[N],tmp[3][N];
char s[N];
int ans[N];
int n;

struct SparseTable
{
	int st[N][20],bin[(1<<18)+5];
	void init(int *arr,int n)
	{
		bin[1]=0;FOR(i,2,1<<18)bin[i]=bin[i>>1]+1;
		FOR(i,1,n)st[i][0]=arr[i];
		FOR(j,1,18)
			FOR(i,1,n-(1<<j)+1)
				st[i][j]=std::min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	}
	int query(int l,int r)
	{
		int k=bin[r-l+1];
		return std::min(st[l][k],st[r-(1<<k)+1][k]);
	}
}St;

void get_SA(char *s,int n,int m)
{
	int *x=tmp[0],*y=tmp[1],*c=tmp[2];
	x[n+1]=y[n+1]=0;
	FOR(i,1,m)c[i]=0;
	FOR(i,1,n)c[x[i]=s[i]]++;
	FOR(i,1,m)c[i]+=c[i-1];
	DOR(i,n,1)sa[c[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1)
	{
		int p=0;
		FOR(i,n-k+1,n)y[++p]=i;
		FOR(i,1,n)if(sa[i]>k)y[++p]=sa[i]-k;
		FOR(i,0,m)c[i]=0;
		FOR(i,1,n)c[x[y[i]]]++;
		FOR(i,1,m)c[i]+=c[i-1];
		DOR(i,n,1)sa[c[x[y[i]]]--]=y[i];
		std::swap(x,y);
		p=x[sa[1]]=1;
		FOR(i,2,n)
			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p;
		if(p==n)break;
		m=p;
	}
	FOR(i,1,n)rk[sa[i]]=i;
	int k=0;
	FOR(i,1,n)
	{
		if(k)k--;
		if(rk[i]==1)continue;
		int j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
		H[rk[i]]=k;
	}
	St.init(H,n);
}

int get_lcp(int x,int y)
{
	if(x==y)return n-x+1;
	x=rk[x],y=rk[y];
	if(x>y)std::swap(x,y);
	return St.query(x+1,y);
}

int main()
{
	int cas=0;
	while(true)
	{
		scanf("%s",s+1);
		if(s[1]=='#')break;
		n=strlen(s+1);
		get_SA(s,n,256);
		int rn=1,x=1,y=1;
		ans[0]=0;
		FOR(i,2,n)if(s[i]<s[x])x=y=i;
		FOR(l,1,n)
		{
			for(int i=1,j;(j=i+l)<=n;i+=l)
			{
				int lcp=get_lcp(i,j),cnt=lcp/l;
				if(cnt+1>rn)rn=cnt+1,ans[ans[0]=1]=l;
				else if(cnt+1==rn&&ans[ans[0]]!=l)ans[++ans[0]]=l;
				int a=(i+lcp-1)-(cnt+1)*l+1,b=(j+lcp-1)-(cnt+1)*l+1;
				if(a>=1&&b>=1)
				{
					lcp=get_lcp(a,b),cnt=lcp/l;
					if(cnt+1>rn)rn=cnt+1,ans[ans[0]=1]=l;
					else if(cnt+1==rn&&ans[ans[0]]!=l)ans[++ans[0]]=l;
				}
			}
		}
		bool flg=0;
		FOR(i,1,n)FOR(j,1,ans[0])
		{
			if(flg)break;
			if(get_lcp(sa[i],sa[i]+ans[j])>=(rn-1)*ans[j])
			{
				x=sa[i],y=sa[i]+rn*ans[j]-1;
				flg=1;
			}
		}
		printf("Case %d: ",++cas);
		FOR(i,x,y)putchar(s[i]);
		puts("");
	}
	return 0;
}
posted @ 2019-03-23 11:51  Paulliant  阅读(184)  评论(0编辑  收藏  举报