见《上》:https://blog.csdn.net/fengqiyuka/article/details/94589605
背景
- 没有背景
- 我们发现,如果只有SA与rank,真是除了求排名什么也做不了。
- 哈哈哈,其实SA与rank只是铺垫,后缀数组最厉害的地方就要展现——
最长公共前缀
- 先来一个最简单的问题:给定一个字符串,求出现最少2次的字符串最长长度。
- 看罢,你喊道——这不是很简单吗?直接
O(n3)或
O(n2)搞定啊!
- 少年,你落伍了!现在已经是log的时代!!
(好吧是O(n) 的时代)
- 在机智地把这道题与后缀联系起来之后,我们发现这道题其实就是求后缀之间的最长公共前缀!
- 例如一个子串在字符串s中的位置分别是
[a,b]和
[c,d],那么这个子串其实就是以a为开头的后缀与以c为开头的前缀的公共前缀。
- 我们发现,之前学过的后缀数组SA与rank,几乎什么用也没有。
- 于是乎,一个全新的后缀数组登场了!!
(好吧是两个)
height与h
-
首先让我们清楚这两个后缀数组的定义。
-
定义
LCP(i,j)为以sa[i]开头的后缀和以sa[j]开头的后缀的最长公共前缀。
-
而
height[i]指的是排名为i的后缀与排名为i-1的后缀的最长公共前缀,即
LCP(i−1,i)。
-
h[i]指的则是以i为开头的后缀与排名在它前一位的后缀的最长公共前缀,即
LCP(rank[i]−1,rank[i])。
-
h[i]很绕,必须先弄清楚定义,后面的东西才可以明白。
-
给出两个等式
height[i]=h[SA[i]],h[i]=height[rank[i]]。
LCP的性质
- 重点部分来了,之所以height与h可以快速求出,是因为LCP有很多神奇的性质。
- a.
LCP(i,j)>=LCP(i,k),当
1<=i<=j<=k<=n。
- 我们设
LCP(i,j)=p,则
s[i+p]̸=s[j+p]。因为
i的排在
j前面,所以
s[i+p]<s[j+p],又因为
j在
k前面,若
LCP(i,k)>p,则必有
s[i+l]=s[k+l](0<=l<=p),通过比较可以的出
k比
j小,则假设不成立,得证。
- b.
LCP(j,i)>=LCP(k,i),当
1<=k<=j<=i<=n。
- 证明方法类似。
- c.若
LCP(rank[i],rank[j])≥1且
rank[j]<rank[i],则
rank[j+1]<rank[i+1],且
LCP(rank[j+1],rank[i+1])=rank([i],rank[j])−1。
- 这个其实也是比较显然的。
- 由已知我们可以得到以i开头后缀与以j开头后缀第一次出现不同的位置在
s[i+k]与
s[j+k]处,则
k>=1,因为
rank[j]<rank[i],所以
s[j+k]<s[i+k],比较可得
rank[j+1]<rank[i+1]。
- 后面这个等式就不用说了吧…
终极性质
- 很好,现在让我们说出height与h最重要的性质:
-
h[i]>=h[i−1]−1
- 首先我们设排在
i−1前一位的是
k,排在
i前一位的是
j。即
h[i]=LCP(rank[i],rank[j]),
h[i−1]=LCP(rank[i−1],rank[k])。
- 分类讨论模式启动…
- 第一种,
h[i−1]=0,显然。
- 第二种,
h[i−1]≥1,则
LCP(rank[i−1],rank[k])≥1。
- 由性质c我们可以得到,由于
rank[k]<rank[i−1],则
rank[k+1]<rank[i],LCP(rank[k+1],rank[i])=LCP(rank[k],rank[i−1])−1=h[i−1]−1。
- 因为
rank[j]=rank[i]−1,所以
rank[k+1]<=rank[j],有性质
b可得:设
A=rank[k+1],B=rank[j],C=rank[i],则
1<=A<=B<=C<=n。
- 所以
LCP(B,C)>=LCP(A,C),所以
LCP(rank[j],rank[i])>=LCP(rank[k+1],rank[i])=h[i−1]+1。得证。
- 综上所述,
h[i]>=h[i−1]+1。
求法
- 有了刚才那个性质之后,h数组就很好求了吧!
- 假设已经知道了
h[i−1],我们只要从
h[i−1]−1开始匹配来求出
h[i]即可。
- 求出了h数组,height数组也就很好求了。
刚才那道题
- 还记得那道题目吗?
- 给定一个字符串,求出现最少2次的字符串最长长度。
- 显然求height数组的最大值即可。
代码
- 没有代码,裸题太不好玩了。
- 大家可以搜一搜与此有关的题目,大把大把。