POJ 2406 Power Strings ( 后缀数组 连续重复字串 )

题意 : 给定一个字符串L , 已知这个字符串是有某个字符串S重复R次得到的,求R的最大值。

思路 : 假设字符串S 的长度为 l ,那么先枚举所有 l 满足 len % l == 0 ,  然后在验证 LCP( 0 , l ) = len - l , 如果成立 , 那么 len / l 就是答案 。 
为什么这样就能成立 ? 根据 LCP( 0 , l ) == len - l ,那么我们可以得到一些等式 :

L( 0 , l ) = L( l , 2 * l ) , L( l , 2 * l ) = L( 2 * l , 3 * l ) ... L(len-2*l,len-*l) = L( len - l , len  ) 

我们定义 L( a , b ) 为 字符串L的子串 : L[a] , L[a+1] ... L[b-1]

所以我们可以得到 L( 0 , l ) = L ( l , 2 * l ) = ... L( len-l , len ) , 所以当满足上述条件时 , 字符串L就是由长度为l的前缀重复得到。

那么解法可以有KMP,因为next数组求的就是这个,不过最近学SA,还是用SA写。不过写的时候要几点注意的,求LCP不要用rmq去求,否则会MLE。因为求的LCP都是和后缀0有关的 , 所以先求出后缀0的rank然后往前,往后推一遍就行了。然后不能用da去写,会TLE ... ( 听说暴力都没卡住,结果卡da卡得死死的 ) 。最后用了dc3才勉强过的。


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;

#define maxn 1000005
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int c0(int *r,int a,int b)
{return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
int c12(int k,int *r,int a,int b)
{if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
 else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
void sort(int *r,int *a,int *b,int n,int m)
{
     int i;
     for(i=0;i<n;i++) wv[i]=r[a[i]];
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[wv[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
     return;
}
void dc3(int *r,int *sa,int n,int m)
{
     int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
     r[n]=r[n+1]=0;
     for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
     sort(r+2,wa,wb,tbc,m);
     sort(r+1,wb,wa,tbc,m);
     sort(r,wa,wb,tbc,m);
     for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
     rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
     if(p<tbc) dc3(rn,san,tbc,p);
     else for(i=0;i<tbc;i++) san[rn[i]]=i;
     for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
     if(n%3==1) wb[ta++]=n-1;
     sort(r,wb,wa,ta,m);
     for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
     for(i=0,j=0,p=0;i<ta && j<tbc;p++)
     sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
     for(;i<ta;p++) sa[p]=wa[i++];
     for(;j<tbc;p++) sa[p]=wb[j++];
     return;
}
int Rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     int i,j,k=0;
     for(i=1;i<=n;i++) Rank[sa[i]]=i;
     for(i=0;i<n;height[Rank[i++]]=k)
     for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);
     return;
}  

int sa[maxn*3] , r[maxn*3] ;
char str[maxn] ;

// lcp[i] 为 后缀 0 和 后缀 sa[i] 的 lcp
int lcp[maxn] ;

int main(){
	while( scanf( "%s" , str )!= EOF ) {
		if( strcmp( str , "." ) == 0 ) break;
		int len = strlen( str ) ;
		for( int i = 0 ; i < len ; i ++ ) r[i] = str[i] ; r[len] = 0 ;
		dc3( r , sa , len + 1 , 200 ) ;
		calheight( r , sa , len ) ;
		int ans = 1 ;
		// 后缀0的Rank
		int r = Rank[0] ;
		lcp[r] = len - sa[r] ;
		for( int i = r + 1 ; i <= len ; i ++ ) {
			lcp[i] = min( lcp[i-1] , height[i] ) ;
		}
		for( int i = r ; i > 1 ; i -- ) {
			lcp[i-1] = min( lcp[i] , height[i] ) ;
		}
		for( int i = 1 ; i < len ; i ++ ) {
			if( len % i == 0 && lcp[Rank[i]] == len - i ) {
				ans = len / i ;
				break;
			}
		}
		printf( "%d\n" , ans ) ;
	}
	return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值