pku2774 Long Long Message

第一道后缀数组...多加点注释希望能帮助理解后缀数组

题意:求两个字符串的最长公共子串

思路:串接两个字符串并对其求height,对后缀排序后,有可能包含最长公共子串的两个后缀必然是排序结果中相邻项,并且要求两个后缀的起始位置不在同一字符串中,所以ans为height[i]的最大值且sa[i]与sa[i-1]有且仅有一个的值小于第一个字符串的长度len

 

#include <iostream>
using namespace std;

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

int b[MAXN],array[4][MAXN],*rank,*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];
    rank
=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(rank[sa[0]]=0,i=1;i<n;i++){//sa[i]
        rank[sa[i]]=rank[sa[i-1]];
        
if(str[sa[i]]!=str[sa[i-1]])
            rank[sa[i]]
++;
    }

    
    
for(k=1;k<&& rank[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[rank[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[rank[sa[i]
-k]]--]=sa[i]-k;

        
//长度不足2k的后缀的编号不变
        for(i=n-k;i<n;i++)//i=sa[j]
            nsa[b[rank[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(rank[nsa[i]]!=rank[nsa[i-1]] || rank[nsa[i]+k]!=rank[nsa[i-1]+k])
                nrank[nsa[i]]
++;
        }
        
int *t=sa;sa=nsa;nsa=t;
        t
=rank;rank=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(rank[i]==0)
            height[rank[i]]
=0;
        
else{
            
for(j=sa[rank[i]-1];str[i+k]==str[j+k];k++);
            height[rank[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;
}
posted @ 2008-11-21 16:36  Beetlebum  阅读(1092)  评论(2编辑  收藏  举报