POJ3693 Maximum repetition substring

题目

The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1.

Given a string containing lowercase letters, you are to find a substring of it with maximum repetition number.

大意是,给你一个字符串,让你输出其中重复次数最多的重复连续字串,如果次数相同就输出字典序最小的。

思路

一开始不好想。

首先,枚举串的长度\(L\),然后枚举区间\([1,L]\)\([L+1,2*L]\),会发现,一个连续重复子串总会被截头去尾的被枚举到。

比如cabababc,在枚举到长度\(L=2\)的时候,中间的那段baba就会被枚举到,然而实际上可能是去了头尾的,注意到LCP求出后后面会多出一段,定义\(R=LCP\),那么,R%L就是多出来的一段,可以看成是前面少取了L-R%L这一段,然后判断是不是可以把这一段补上,就完成了第一步:找到一段区间内的最长重复连续子串。

然后考虑第二步:要求字典序最小。

我们把这些可能成为答案的串先存起来,然后按照的顺序一遍扫描,因为sa天然有序,所以就可以得到最终的答案了。

代码

#include<cstdio>
#include<string.h>
#include<algorithm> 
#include<iostream>
#define M 100005
using namespace std;
int sa[M],rk[M],t1[M],t2[M],tmp[M],cnt1[M],cnt2[M],H[M];
struct node{
	int x,id;
	bool operator < (const node&  res)const{
		if(x!=res.x)return x<res.x;
		return id<res.id;
	}
}A[M];
void Init(char *s,int n){
	for(int i=1;i<=n;i++)A[i]=(node){s[i],i};
	sort(A+1,A+n+1);
	for(int i=1;i<=n;i++)sa[i]=A[i].id; 
	rk[sa[1]]=1;
	for(int i=2;i<=n;i++){
		rk[sa[i]]=rk[sa[i-1]];
		if(s[sa[i]]!=s[sa[i-1]])rk[sa[i]]++; 
	}
	for(int l=1;rk[sa[n]]<n;l<<=1){
		for(int i=0;i<=n;i++)cnt1[i]=cnt2[i]=0;
		for(int i=1;i<=n;i++)cnt1[t1[i]=rk[i]]++,cnt2[t2[i]=(l+i<=n)?rk[i+l]:0]++;
		for(int i=1;i<=n;i++)cnt1[i]+=cnt1[i-1],cnt2[i]+=cnt2[i-1];
		for(int i=n;i>=1;i--)tmp[cnt2[t2[i]]--]=i;
		for(int i=n;i>=1;i--)sa[cnt1[t1[tmp[i]]]--]=tmp[i];
		rk[sa[1]]=1;
		for(int i=2;i<=n;i++){
			rk[sa[i]]=rk[sa[i-1]];
			if(t1[sa[i]]!=t1[sa[i-1]]||t2[sa[i]]!=t2[sa[i-1]])rk[sa[i]]++;
		}
	}
	for(int i=1,j=0;i<=n;i++){
		j-=j>0;
		while(s[i+j]==s[sa[rk[i]-1]+j])j++;
		H[rk[i]]=j;
	}
}
struct Stable{
	int mn[M][21],Log[M];
	void Init(int n){
		for(int i=1;i<=n;i++){
			mn[i][0]=H[i];
			if(i>1)Log[i]=Log[i>>1]+1;
		}
		for(int j=1;j<21;j++)
			for(int i=1;i+(1<<j-1)<=n;i++)
				mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
	}
	int Query(int l,int r){
		int t=Log[r-l+1];
		return min(mn[l][t],mn[r-(1<<t)+1][t]);
	}
}st;
int LCP(int l,int r){
	l=rk[l],r=rk[r];
	if(l>r)swap(l,r);
	return st.Query(l+1,r);
}
char S[M];
int n,cas=0,a[M],cnt;
int main(){
	while(scanf("%s",S+1)&&S[1]!='#'){
		n=strlen(S+1);
		Init(S,n);
		st.Init(n);
		int ans=0;
		for(int L=1;L<=n;L++){
			for(int i=1;i<=n;i+=L){
				int R=LCP(i,i+L),step=R/L+1,k=i-(L-R%L);
				if(k>=0&&R%L)if(LCP(k,k+L)>=R)step++;
				if(step>ans){ans=step;cnt=0;a[cnt++]=L;}
				else if(step==ans)a[cnt++]=L;
			}
		}
		int len=-1,st;
		for(int i=1;i<=n&&len==-1;i++){
			for(int j=0;j<cnt;j++){
				int L=a[j];
				if(LCP(sa[i],sa[i]+L)>=(ans-1)*L){
					len=L;st=sa[i];break;
				}
			}
		}		
		printf("Case %d: ",++cas);
		for(int i=st;i<st+len*ans;i++)printf("%c",S[i]);
		putchar('\n');
		for(int i=1;i<=n;i++)S[i]=0;
	}
	return 0;
}
posted @ 2019-07-17 17:03  zeroy0410  阅读(145)  评论(0)    收藏  举报