解释及作用
是后缀数组中相邻两个字符串的lcp(最长公共前缀),可以在O(n)的时间内高效求出,帮助做一些字符串匹配问题。
h[i]=lcp(st[i],st[i+1])(st表示后缀数组中表示的字符串)
实现方法
如果已经知道后缀数组中i与i+1的lcp为h,那么i代表的字符串与i+1代表的字符串去掉首字母后的lcp为h-1.根据这个我们可以发现,如果知道i与后缀数组中在它后一个的lcp为k,那么它去掉首字母后的字符串与其在后缀数组中的后一个的lcp大于等于k-1.
例如对于字符串abcefabc,我们知道abcefabc与abc的lcp为3.
那么bcefabc与bc的lcp大于等于3-1.
利用这一点就可以O(n)求出高度数组。
例题poj 2217
题意
给出两个字符串,求出它们的最长公共子串(不是子序列,要求连续)
做法
可以先转化一下,对于一个字符串,可以用高度数组快速求出出现至少两次的最长字串,因此此题可以先将两个字符串并成一串,中间加一个永远不会出现的字符,比如字符0,然后求出高度数组,在两个相同字符串分别出现在字符0左右两边的长度中取最大值即为答案。
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#define N 20010
using namespace std;
int T,st[N],rank[N],tmp[N],n,l,lcp[N],ans,h,mid;
string a,b;
inline bool cmp(int u,int v)
{
if(rank[u]!=rank[v]) return rank[u]<rank[v];
int nu,nv;
nu=(u+l>n)?-1:rank[u+l];
nv=(v+l>n)?-1:rank[v+l];
return nu<nv;
}
int main()
{
int i,j;
cin>>T;
char ch=getchar();
while(T--)
{
getline(cin,a);
mid=a.size()+1;
getline(cin,b);
a=a+(char)0+b;
n=a.size();
for(i=1;i<=n;i++)
{
rank[i]=a[i-1];
st[i]=i;
}
for(l=1;l<=n;l<<=1)
{
sort(st+1,st+n+1,cmp);
tmp[st[1]]=1;
for(i=2;i<=n;i++)
{
tmp[st[i]]=tmp[st[i-1]]+cmp(st[i-1],st[i]);
}
for(i=1;i<=n;i++)
{
rank[i]=tmp[i];
}
}
lcp[0]=ans=h=0;
for(i=1;i<=n;i++)
{
j=st[rank[i]-1];
if(h) h--;
for(;i+h<=n&&j+h<=n&&a[i+h-1]==a[j+h-1];h++);
lcp[rank[i]-1]=h;
}
for(i=1;i<n;i++)
{
if((st[i]<mid)!=(st[i+1]<mid)) ans=max(ans,lcp[i]);
}
printf("Nejdelsi spolecny retezec ma delku %d.\n",ans);
}
}