链接:http://poj.org/problem?id=2774
题意:求两个字符串的最长公共子串长度
第一次后缀数组,模板之。
利用后缀数组求公共子串,首先将两个字符串合并成一个字符串,两个字符串之间加上一个比字符串里所有字符都小的字符。
求出该字符串的height[]数组,当且仅当suffix[sa[i]]和suffix[sa[i-1]]不是同一个字符串的后缀数组时,求出最大的height[i],为答案。
基本定义:
s[i]表示字符串,s[i]最好不要小于0,试过换成数字算,有负数就错了
sa[i]=j表示排第i的下标是j,比如sa[2]=3,排第二的下标元素是3。
rank[i]=j表示下标是i的排第j,比如rank[3]=2,下标元素是3的排第二。sa与rank为互逆关系,即sa[rank[i]]=i。
suffix[i]表示字符串从i开始的后缀
height[i]表示suffix[sa[i]]与suffix[sa[i-1]]的最长公共子串,即排名相邻的两个数组的公共子串长度。
注意::height数组的值应该是从height[1]开始的,而且height[1]应该是等于0的。原因是,因为我们在字符串后面添加了一个0号字符,所以它必然是最小的一个后缀。
要注意height数组的范围应该是[1..n]。所以调用时应该是setheight(sa,len)而不是calheight(sa,len+1)。setheight过程中,对rank数组求值的for语句的初始语句是i=1而不是i=0的原因,和上面说的类似,因为sa[0]总是等于那个已经失去作用的0号字符,所以没必要求出其rank值。
#include<stdio.h>
#include<math.h>
#include<string.h>
#define N 200060
char s[N];
int len; //数组长度
int wa[N],wv[N],height[N],wb[N],ws[N];
int rank[N],sa[N];
int cmp(int y[],int a,int b,int j)
{
return (y[a]==y[b]&&y[a+j]==y[j+b]);
}
void setsa(int sa[],int n,int m) //这里的n是数组长度len+1,数组末尾补了一个0
{
int i,j,*x=wa,*y=wb,*t,p;
//下面三个for为基数排序,按照大小与前后排序
memset(ws,0,sizeof(ws));
for(i=0;i<n;i++)
ws[x[i]=s[i]]++;
for(i=1;i<m;i++)
ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) //注意是从n-1开始
sa[--ws[x[i]]]=i;
for(j=1,p=1;p<n;m=p,j*=2)
{
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]];
//基数排序
memset(ws,0,sizeof(ws));
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]; //注意这里的下标换成y[i]
t=x;x=y;y=t; //交换x和y
for(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++);
}
}
void setheight(int sa[],int n) //这里的n是数组长度len
{
int i,j,k=0;
for(i=1;i<=n;i++) //求出rank,由于sa[0]是在数组上补回去的0,所以sa[0]不属于数组,所以从sa[1]开始
rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];s[i+k]==s[j+k];k++);
}
int main()
{
int i,j,n,m,max;
gets(s);
n=strlen(s);
s[n]='$';
gets(s+n+1);
len=strlen(s);
s[len]=0;
setsa(sa,len+1,200); //建sa数组
setheight(sa,len); //建height数组
for(i=2,max=-100000;i<=len;i++)
if( ( (sa[i]<n&&sa[i-1]>n)||(sa[i-1]<n&&sa[i]>n) )&&height[i]>max)
max=height[i];
printf("%d\n",max);
return 0;
}