【POJ3693】Maximum repetition substring 后缀数组恶心题

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42677359
其实我就是觉得原创的访问量比未授权盗版多有点不爽233。。。

题意:

给一个字符串,然后找一个子串,使子串满足其中连续重复子串最多。

比如ababab,重复次数为3,ababa,重复次数为1(abab是两次)


恶心在于还要输出最小字典序。


题解网上都有,不发了。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define LOGN 20
#define inf 0x3f3f3f3f
using namespace std;
char s[N];
int len;
int sa[N],rank[N],height[N];
int sa2[N],rank2[N],height2[N];

int stk[N],top,cnt[N];
int val[N],_val[N];
inline bool cmp(int x,int y,int hl)
{return val[x]==val[y]&&((x+hl>=len&&y+hl>=len)||(x+hl<len&&y+hl<len&&val[x+hl]==val[y+hl]));}
void SA()
{
	int i,j,k,hl,lim=300;
	for(i=0;i<lim;i++)cnt[i]=0;
	for(i=0;i<len;i++)cnt[val[i]=s[i]]++;
	for(i=1;i<lim;i++)cnt[i]+=cnt[i-1];
	for(i=len-1;i>=0;i--)sa[--cnt[val[i]]]=i;

	for(k=0;;k++)
	{
		top=0,hl=1<<k;
		for(i=0;i<len;i++)if(sa[i]+hl>=len)stk[++top]=sa[i];
		for(i=0;i<len;i++)if(sa[i]>=hl)stk[++top]=sa[i]-hl;

		for(i=0;i<lim;i++)cnt[i]=0;
		for(i=0;i<len;i++)cnt[val[i]]++;
		for(i=1;i<lim;i++)cnt[i]+=cnt[i-1];
		for(i=len;i>0;i--)sa[--cnt[val[stk[i]]]]=stk[i];

		for(i=lim=0;i<len;lim++)
		{
			for(j=i;j<len-1&&cmp(sa[j],sa[j+1],hl);j++);
			for(;i<=j;i++)_val[sa[i]]=lim;
		}
		for(i=0;i<len;i++)val[i]=_val[i];
		if(lim==len)break;
	}
	for(i=0;i<len;i++)rank[sa[i]]=i;
	for(k=i=0;i<len;i++)
	{
		if(k)k--;
		if(!rank[i])continue;
		while(s[i+k]==s[sa[rank[i]-1]+k])k++;
		height[rank[i]]=k;
	}
	lim=300;
	for(i=0;i<lim;i++)cnt[i]=0;
	for(i=0;i<len;i++)cnt[val[i]=s[len-i-1]]++;
	for(i=1;i<lim;i++)cnt[i]+=cnt[i-1];
	for(i=len-1;i>=0;i--)sa2[--cnt[val[i]]]=i;

	for(k=0;;k++)
	{
		top=0,hl=1<<k;
		for(i=0;i<len;i++)if(sa2[i]+hl>=len)stk[++top]=sa2[i];
		for(i=0;i<len;i++)if(sa2[i]>=hl)stk[++top]=sa2[i]-hl;

		for(i=0;i<lim;i++)cnt[i]=0;
		for(i=0;i<len;i++)cnt[val[i]]++;
		for(i=1;i<lim;i++)cnt[i]+=cnt[i-1];
		for(i=len;i>0;i--)sa2[--cnt[val[stk[i]]]]=stk[i];

		for(i=lim=0;i<len;lim++)
		{
			for(j=i;j<len-1&&cmp(sa2[j],sa2[j+1],hl);j++);
			for(;i<=j;i++)_val[sa2[i]]=lim;
		}
		for(i=0;i<len;i++)val[i]=_val[i];
		if(lim==len)break;
	}
	for(i=0;i<len;i++)rank2[sa2[i]]=i;
	for(k=i=0;i<len;i++)
	{
		if(k)k--;
		if(!rank2[i])continue;
		while(s[len-i-k-1]==s[len-sa2[rank2[i]-1]-k-1])k++;
		height2[rank2[i]]=k;
	}
}
int st[N][LOGN],St[N][LOGN],logf[N];
int ST[N][LOGN],sT[N][LOGN];
void RMQ_init()
{
	int i,j,k,sk;
	for(i=0;i<len;i++)
	{
		st[i][0]=height[i];
		St[i][0]=height2[i];
		sT[i][0]=rank[i];
		ST[i][0]=i;
	}
	for(i=2;i<N;i++)logf[i]=logf[i>>1]+1;
	for(j=1;k=(1<<j),k<len;j++)
		for(sk=(k>>1),i=0;i+k-1<len;i++)
		{
			st[i][j]=min(st[i][j-1],st[i+sk][j-1]),
			St[i][j]=min(St[i][j-1],St[i+sk][j-1]);
			if(sT[i][j-1]<sT[i+sk][j-1])
			{
				sT[i][j]=sT[i][j-1];
				ST[i][j]=ST[i][j-1];
			}
			else {
				sT[i][j]=sT[i+sk][j-1];
				ST[i][j]=ST[i+sk][j-1];
			}
		}
			
}
inline int querypre(int L,int R) // 两个点最长相同前串
{
	if(L>R)swap(L,R);L++;
	int ll=logf[R-L];
	return min(St[L][ll],St[R-(1<<ll)+1][ll]);
}
inline int querysuc(int L,int R) // 两个点最长相同后串
{
	if(L>R)swap(L,R);L++;
	int ll=logf[R-L];
	return min(st[L][ll],st[R-(1<<ll)+1][ll]);
}
inline int query(int L,int R) // 求一段区间内最小rank
{
	if(L>R)swap(L,R);
	int ll=logf[R-L];
	return sT[L][ll]<sT[R-(1<<ll)+1][ll]?ST[L][ll]:ST[R-(1<<ll)+1][ll];
}
int ans,id,id2;
int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k,g=0;
	int l,r,pre,suc,ll;
	while(scanf("%s",s),s[0]!='#')
	{
		printf("Case %d: ",++g);
		len=strlen(s);
		SA();
		RMQ_init();
		ans=1,id=0,id2=1;
		for(i=len>>1;i;i--) // 枚举 重复部分 长度
		{
			for(j=0;j+i<len;j+=i)
			{
				pre=querypre(rank2[len-j-1],rank2[len-j-i-1]);
				suc=querysuc(rank[j],rank[j+i]);
				ll=(pre&&suc)?(pre+suc-1):(pre+suc);
				if(k=ll/i)
				{
					l=j-pre+(pre?1:0);
					r=l+ll-k*i;
					int mx=query(l,r);
					k++;
					if(ans<k||(ans==k&&rank[id]>=rank[mx]))
						ans=k,id=mx,id2=id+k*i;
				}
			}
		}
		if(ans==1)printf("%c\n",s[sa[0]]);
		for(i=id;i<id2;i++)printf("%c",s[i]);
		puts("");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值