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<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[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;
}
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[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;
}