POJ-3693 Maximum repetition substring (后缀数组)

POJ-3693 Maximum repetition substring (后缀数组)

题意:求连续重复次数最多的子串

一个连续重复子串可以描述为\(l,r,len\)(端点,循环节长度)

\(O(n^2)\):直接枚举\(l,len\),然后求\(l\)\(l+len\)\(LCP\),就能得到最大的\(r\)

考虑不一一枚举\(l\)

枚举\(len\)之后,从一个点开始向前取\(LCP\),向后取\(LCP\),就能够拼出一个完整的循环串

用这种方法可以去掉大量重复枚举,只枚举\(l=k\cdot len (k\in Z)\)

用ST表查询可以做到\(O(n \ln n)\)

细节似乎蛮多的,代码很烦

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cctype>
#include<cstring>
#include<cassert>
using namespace std;

namespace Folder{
#define reg register
#define pb push_back
	typedef long long ll;
	typedef unsigned long long ull;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
	template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
	template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
	char IO;
	//template <class T=int> 
	int rd(){
		int s=0;
		int f=0;
		while(!isdigit(IO=getchar())) f|=(IO=='-');
		do s=(s<<1)+(s<<3)+(IO^'0');
		while(isdigit(IO=getchar()));
		return f?-s:s;
	}
}
using namespace Folder;


const int N=1e5+10,INF=1e9;
int n;
char s[N];
int kase;
#define ms(a) memset(a,0,sizeof a)
struct Sparse_Table{
	int s[20][N],Log[N];
	void PreMake(const int *a){ 
		rep(i,2,n) Log[i]=Log[i>>1]+1;
		rep(i,1,n) s[0][i]=a[i];
		rep(i,1,Log[n]) {
			int len=(1<<(i-1));
			rep(j,1,n-(1<<i)+1) s[i][j]=min(s[i-1][j],s[i-1][j+len]);
		}
	}
	int Que(int l,int r) {
		int d=Log[r-l+1];
		return min(s[d][l],s[d][r-(1<<d)+1]);
	}
};

struct Suffix_Array{
	int cnt[N],rk[N<<1],sa[N],tmp[N],lcp[N];
	Sparse_Table ST;
	void PreMake(char *s){
		n=strlen(s+1);
		memset(cnt,0,800);
		rep(i,1,n) cnt[(int)s[i]]++;
		rep(i,1,200) cnt[i]+=cnt[i-1];
		rep(i,1,n) rk[i]=cnt[(int)s[i]],sa[i]=i;
		rep(i,n+1,n*2) rk[i]=0;
		for(reg int k=1;k<=n;k<<=1) {
			rep(i,0,n) cnt[i]=0;
			rep(i,1,n) cnt[rk[i+k]]++;
			rep(i,1,n) cnt[i]+=cnt[i-1];
			drep(i,n,1) tmp[cnt[rk[i+k]]--]=i;

			rep(i,0,n) cnt[i]=0;
			rep(i,1,n) cnt[rk[i]]++;
			rep(i,1,n) cnt[i]+=cnt[i-1];
			drep(i,n,1) sa[cnt[rk[tmp[i]]]--]=tmp[i];

			rep(i,1,n) tmp[sa[i]]=tmp[sa[i-1]]+(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+k]!=rk[sa[i-1]+k]);
			rep(i,1,n) rk[i]=tmp[i];
		}
		int h=0;
		memset(lcp,0,(n+2)*4);
		rep(i,1,n) {
			int j=sa[rk[i]-1];
			if(h) h--;
			while(i+h<=n && j+h<=n && s[i+h]==s[j+h]) h++;
			lcp[rk[i]-1]=h;
		}
		ST.PreMake(lcp);
	}
	int Que(int x,int y){
		if(x==y) return n-x+1;
		if(rk[x]>rk[y]) swap(x,y);
		return ST.Que(rk[x],rk[y]-1);
	}
}SA,RSA;
Sparse_Table ST;


int main(){
	while(~scanf("%s",s+1) && s[1]!='#') {
		n=strlen(s+1);
		SA.PreMake(s),reverse(s+1,s+n+1),RSA.PreMake(s);
		ST.PreMake(SA.rk);
		int ans=0,st,ed;
		rep(i,1,n) {
			rep(j,1,n) {
				int a=i*(j-1)+1,b=i*j+1;
				if(b>n) break;
				int len=SA.Que(a,b)+RSA.Que(n-a+1,n-b+1)-1; // 向前和向后找LCP
				if(len<0) continue;
				if(len<i) continue;
				int l=a-RSA.Que(n-a+1,n-b+1)+1,r=b+SA.Que(a,b)-1;
				int p=SA.sa[ST.Que(l,r-(len/i+1)*i+1)];
				l=p,r=p+(len/i+1)*i-1;
				if(len/i+1>ans) ans=len/i+1,st=l,ed=r;
				else if(len/i+1==ans && (SA.rk[st]>SA.rk[l]||(SA.rk[st]==SA.rk[l]&& r-l+1<ed-st+1) ) ) st=l,ed=r;
			}
		}
		if(!ans) ans=1,st=SA.sa[1],ed=SA.sa[1];
		reverse(s+1,s+n+1);
		printf("Case %d: ",++kase);
		rep(i,st,ed) putchar(s[i]);
		putchar('\n');
	}
}

posted @ 2020-01-19 15:16  chasedeath  阅读(85)  评论(0编辑  收藏  举报