问题引入
求第i位开始的后缀和第j位开始的后缀的最长公共前缀的长度?
高度数组&&RMQ
求出高度数组lcp,假设rank[i]小于rank[j],那么后缀i和后缀j的LCP值等于min{lcp[rank[i],lcp[rank[i+1],…lcp[rank[j]-1}.
预处理RMQ即可O(1)查询LCP值。但由于是预处理RMQ,无法动态回答LCP回答。也就是说一旦题目中有对字符串修改的操作,则此方法无法解决。
基于哈希的LCP
给每一个子串一个哈希值,这样子串改变时只需对应地修改哈希值即可。
定h(i)表示后缀S[i…]的哈希值。
定义hash(i,L)表示以i为起始位置、长度为L的子串的哈希值。
h(n-1)=s[n-1] 边界
h(i)=h(i+1)*x+s[i] 递推
一般地,
h[i]=s[i]+s[i+1]*x+s[i+2]*x^2+...+s[n-1]*x^(n-1-i);
hash(i,L)的哈希值定义如下
hash(i,L)=s[i]+s[i+1]*x+s[i+2]*x^2+...+s[i+L-1]*x^(L-1)
推导后,
hash(i,L)=h[i]-h[i+L]*x^L;
所以预处理h和x^L后就可以在常数时间内计算出hash(i,L)。hash值可能很大,这里用unsigned long long去存,这样在算术运算时会自然溢出,相当于模2^64.
解决问题:
二分答案L,如果hash(i,L)==hash(j,L),那么就认为以LCP(i,j)>=len.
模板
const int x=123;//随便取一个x
unsigned long long h[maxn],xp[maxn];
int n;
string s;
void get_hxp()
{
n=s.size();
h[n]=0;
for(int i=n-1;i>=0;i--)
h[i]=h[i+1]*x+s[i]-'a';
xp[0]=1;
for(int i=1;i<=n;i++)
xp[i]=xp[i-1]*x;
}
unsigned long long Hash(int i,int L)
{
return h(i)-h(i+L)*xp[L];
}