您的位置:首页 > 其它

pku2774 Long Long Message

2008-11-21 16:36 155 查看
第一道后缀数组...多加点注释希望能帮助理解后缀数组


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

思路:串接两个字符串并对其求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<n && 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
='\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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: