poj 3693/hdu 2459 Maximum repetition substring spoj 687. Repeats ( 后缀数组 重复次数最多的连续重复子串)

题意 : 给定一个字符串,求重复次数最多的连续重复子串。POJ 3693 还要求输出字典序最小的重复次数最多的连续重复子串。

思路 : 论文中的方法太好了...想了半天都没点思路 ... 看了论文中的解法才有点想法.

 首先我们要枚举长度L , 然后求长度为L的子串最多能连续出现多少次。首先连续出现1 次是肯定可以的,所以这里只考虑至少 2 次的情况。假设在原字符串中连续出现 2 次,记这个子字符串为 S,那么 S 肯定包括了字符 r[0], r[L], r[L*2],r[L*3], ……中的某相邻的两个。所以只须看字符 r[L*i]和 r[L*(i+1)]往前和往后各能匹配到多远,记这个总长度为 K,那么这里连续出现了 K/L+1 次。最后看最大值是多少。

穷举长度 L 的时间是 n,每次计算的时间是 n/L。所以整个做法的时间复杂度是 O(n/1+n/2+n/3+……+n/n)=O(nlogn)

其中两个位置的往后匹配只要求原字符串的LCP,往前匹配,我的做法是将原字符串反过来之后贴在原字符串之后,在sa求LCP

POJ 3693 还需要求最小字典序 。我的做法是从第一个匹配的字符开始枚举,即从第一个位置开始匹配,求得的LCP为K,然后我们往后枚举起始点,那么这个LCP就会一步步减少,直到 LCP/L-1 小于原来的值位置,那么取这里面的最小字典序解,才能用来更新答案。


下面是POJ 3693 的代码 :

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

#define maxn 200005
#define maxm 200005

int wa[maxn],wb[maxn],wv[maxn],wt[maxn];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(char *r,int *sa,int n,int m)
{
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<m;i++) wt[i]=0;
     for(i=0;i<n;i++) wt[x[i]=r[i]]++;
     for(i=1;i<m;i++) wt[i]+=wt[i-1];
     for(i=n-1;i>=0;i--) sa[--wt[x[i]]]=i;
     for(j=1,p=1;p<n;j*=2,m=p)
     {
       for(p=0,i=n-j;i<n;i++) y[p++]=i;
       for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
       for(i=0;i<n;i++) wv[i]=x[y[i]];
       for(i=0;i<m;i++) wt[i]=0;
       for(i=0;i<n;i++) wt[wv[i]]++;
       for(i=1;i<m;i++) wt[i]+=wt[i-1];
       for(i=n-1;i>=0;i--) sa[--wt[wv[i]]]=y[i];
       for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
       x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     }
     return;
}
int Rank[maxn],height[maxn];
void calheight(char *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 RMQ[maxn];
int mm[maxn];
int best[20][maxn];
void initRMQ(int n)
{
     int i,j,a,b;
	 for(int i=1;i<=n;i++)
		RMQ[i] = height[i] ;
     for(mm[0]=-1,i=1;i<=n;i++)
     mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
     for(i=1;i<=n;i++) best[0][i]=i;
     for(i=1;i<=mm[n];i++)
     for(j=1;j<=n+1-(1<<i);j++)
     {
       a=best[i-1][j];
       b=best[i-1][j+(1<<(i-1))];
       if(RMQ[a]<RMQ[b]) best[i][j]=a;
       else best[i][j]=b;
     }
     return;
}
int askRMQ(int a,int b)
{
    int t;
    t=mm[b-a+1];b-=(1<<t)-1;
    a=best[t][a];b=best[t][b];
    return RMQ[a]<RMQ[b]?a:b;
}
int lcp(int a,int b)
{
    int t;
    a=Rank[a];b=Rank[b];
    if(a>b) {t=a;a=b;b=t;}
    return(height[askRMQ(a+1,b)]);
}

char str[maxn] , str2[maxn] ;
int sa[maxn] ;

char ans[maxn] , tmp[maxn] ;

int main(){
	int casn = 1 ;
	while( scanf( "%s" , str ) != EOF ) {
		if( strcmp( str , "#" ) == 0 ) break;
		ans[0] = 254 ;
		ans[1] = 0 ;
        strcpy( str2 , str ) ;  
        //strrev( str2 ) ;
		int Len ;
        int len = Len = strlen( str ) ;  
        str[len] = '$' ; str[len+1] = 0 ;  
        //strcat( str , str2 ) ;  
        for( int i = 0 ; i < len ;i ++ ) {  
            str[len+i+1] = str2[len-i-1] ;  
        }  
        str[len+len+1] = 0 ;  
        len = strlen( str ) ;  
        // 最后再加一个之前没有出现过的字符 , 避免了判断边界情况  
        str[len] = '&' ; str[len+1] = 0 ;  
        len ++ ;  
        da( str , sa , len + 1 , 200 ) ;  
        calheight( str , sa , len ) ;  
        initRMQ( len ) ;  
        len -- ;
		// 枚举 L
		int Max = 0 ;
		for( int L = 1 ; L <= Len ; L ++ ) {
			for( int i = 0 ; ( i + 1 ) * L < Len ; i ++ ) {
				int K1 = lcp( i * L , ( i + 1 ) * L ) ;
				int K2 = lcp( len - i * L - 1 , len - ( i + 1 ) * L - 1 ) ;
				int K = K1 + K2 ;
				if( K > 0 ) K -- ;
				int s = i * L - K2 + 1 ;
				int cnt = K / L + 1 ;
				
				if( cnt >= Max ) {
					//tmp = string( str+s , str + s + cnt * L ) ;
					if( cnt > Max ) { Max = cnt ; ans[0] = 254 ; ans[1] = 0 ; }
					int j = 0 ;
					for( int i = s ; i < s + cnt * L ; i ++ ) {
						tmp[j++] = str[i] ;
					}
					tmp[j] = 0 ;
					if( strcmp( ans, tmp ) > 0 ) {
						strcpy( ans , tmp ) ;
					}
					int i = 1 ;
					while( ( K - i ) / L + 1 == cnt ) {
						tmp[j++] = str[s + cnt * L + i -1] ;
						tmp[j] = 0 ;
						if( strcmp( ans , tmp + i ) > 0 ) {
							strcpy( ans , tmp + i ) ;
						}
						i ++ ;
					}
				}
			}
		}
		printf( "Case %d: %s\n" , casn ++ , ans ) ;
	}
	return 0 ;
}

//  ccaaccaaccaacc


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值