pku2774 求最长公共子串

嘿嘿,用大牛的代码过的,我觉得,要理解这个好难呀,但要是用了模板计算后缀数组sa[],名次数组rank[],还有height[],height[]是后缀数组的关键所在,用来模板计算出这三个数组,后缀数组的应用也就拓展得开了哦

看下模板吧,不过这个版本用了657ms耶,不是很快的说

#include <iostream>
using namespace std;

#define MAXN 200010// max{str.len,256}

int b[MAXN],array[4][MAXN],*rank1,*nrank,*sa,*nsa,height[MAXN],n,len;
char str[MAXN],str1[MAXN/2];

void make_sa(){
    int i,k;
    n=strlen(str);

    sa=array[0];
    nsa=array[1];
    rank1=array[2];
    nrank=array[3];

    //求sa_1
    //计数排序
    memset(b,0,sizeof(b));
    for(i=0;i<n;i++)
        b[str[i]]++;
    for(i=1;i<=256;i++)
        b[i]+=b[i-1];
    for(i=n-1;i>=0;i--)//str[i]
        sa[--b[str[i]]]=i;

    //求rank_1
    for(rank1[sa[0]]=0,i=1;i<n;i++){//sa[i]
        rank1[sa[i]]=rank1[sa[i-1]];
        if(str[sa[i]]!=str[sa[i-1]])
            rank1[sa[i]]++;
    }

    
    for(k=1;k<n && rank1[sa[n-1]]<n-1;k*=2){
        //求sa_2k
        //桶排序
        //只保存每个桶的尾部后缀的编号作为索引(编号为i的后缀即suffix(sa[i]))。
        //注意到在sa_n和rank_n构造完毕之前,sa_k和rank_k不一定是互为反函数:
        //sa-k[i]=j总是双射;rank-k[i]=j可能存在多个i映射到一个j上。
        //反映到算法上就是所有k前缀相等的后缀都放在一个桶内。
        for(i=0;i<n;i++)//sa[i]
            b[rank1[sa[i]]]=i;

        //基数排序
        //由于2k前缀的后半段已排好序,对所有后缀的2k前缀的排序结果就是对2k前缀
        //的前半段的稳定排序结果。
        //注意到一个后缀的2k前缀的前半段实质是某一个后缀的k前缀,所有后缀的k前
        //缀的排序结果已由桶排得到
        for(i=n-1;i>=0;i--)//sa[i]
            if(sa[i]-k>=0)
                nsa[b[rank1[sa[i]-k]]--]=sa[i]-k;

        //长度不足2k的后缀的编号不变
        for(i=n-k;i<n;i++)//i=sa[j]
            nsa[b[rank1[i]]--]=i;

        //求rank_2k        
        for(nrank[nsa[0]]=0,i=1;i<n;i++){//nsa[i]
            nrank[nsa[i]]=nrank[nsa[i-1]];
            //仅当两个2k前缀的前半部分和后半部分分别对应相等时两个2k前缀相等
            if(rank1[nsa[i]]!=rank1[nsa[i-1]] || rank1[nsa[i]+k]!=rank1[nsa[i-1]+k])
                nrank[nsa[i]]++;
        }
        int *t=sa;sa=nsa;nsa=t;
        t=rank1;rank1=nrank;nrank=t;
    }
}

void cal_height(){//height[i]定义为LCP(i-1,i),表示名次为i-1和名次为i的后缀的lcp
    int i,j,k;
    //rank和sa互为反函数
    for(i=0,k=0;i<n;i++){//str[i]
        if(rank1[i]==0)
            height[rank1[i]]=0;
        else{
            for(j=sa[rank1[i]-1];str[i+k]==str[j+k];k++);
            height[rank1[i]]=k;//k为当前h[sa[i]]的值
            if(k>0)
                k--;
        }
    }
}


int main(){
    int i,p1,p2,ans;
    while(scanf("%s%s",str,str1)!=EOF){
        //构造"str#str1$"
        len=strlen(str);
        str[len]='#';
        str[len+1]='\0';
        strcat(str,str1);
        n=strlen(str);
        str[n++]='$';
        str[n]='\0';

        make_sa();
        cal_height();

        ans=0;
        for(i=1;i<n;i++){
            if(sa[i]<len)
                p1=1;
            else
                p1=-1;
            if(sa[i-1]<len)
                p2=1;
            else
                p2=-1;
            if(p1*p2<1 && height[i]>ans)
                ans=height[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

另一个模板哦,更快一点

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
  
int const N= 201000;   
int wa[N], wb[N], ws[N], wv[N];   
int sa[N], height[N], rank[N], n,r[N];   
char str[N],str1[N];
  
int cmp( int* r, int a, int b, int L ){   
    return r[a]== r[b] && r[a+ L]== r[b+ L];   
}   
  
void da( int* r, int* sa, int n, int m ){   
    int i, j, p, *x= wa, *y= wb, *t;   
    for( i= 0; i< m; ++i ) ws[i]= 0;   
    for( i= 0; i< n; ++i ) ws[ x[i]= r[i] ]++;   
    for( i= 1; i< m; ++i ) ws[i]+= ws[i-1];   
    for( i= n- 1; i>= 0; i-- ) sa[ --ws[ 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 ) 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-- ) sa[ --ws[ wv[i] ] ]= y[i];   
  
        t= x, x= y, y= t, p= 1; x[ sa[0] ]= 0;   
        for( i= 1; i< n; ++i )   
            x[ sa[i] ]= cmp( y, sa[i-1], sa[i], j )? p- 1: p++;   
    }   
}   
  
void callheight( 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++ );   
}
int main(){
    int i,p1,p2,ans,len1,len2;
    while(scanf("%s%s",str,str1)!=EOF){
        len1=strlen(str);
		for(i=0;i<len1;i++)
			r[i]=static_cast<int>(str[i]);
		r[len1]=1;
        len2=strlen(str1);
		for(i=0;i<len2;i++)
			r[len1+i+1]=static_cast<int>(str1[i]);
		r[len1+len2+1]=0;
		da(r,sa,len1+len2+2,256);
		callheight(r,sa,len1+len2+1);
        ans=0;
		n=len1+len2+1;
        for(i=1;i<=n;i++){
            if(sa[i]<len1)
                p1=1;
            else
                p1=-1;
            if(sa[i-1]<len1)
                p2=1;
            else
                p2=-1;
            if(p1*p2<1 && height[i]>ans)
                ans=height[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2011-05-17 20:45  枕边梦  阅读(232)  评论(0)    收藏  举报