后缀数组
Posted skiceanakacniu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀数组相关的知识,希望对你有一定的参考价值。
后缀数组
对于给定的字符串(s),构建两个数组(sa)和(rk),其中(rk[i])表示(s[i,n])在(s)的所有后缀中的字典序排名,(sa[i])则表示排名(i)的后缀的最左端位置。容易发现有(sa[rk[i]]=rk[sa[i]]=i)。
构造 - 倍增法
一般来说,我们用倍增法(O(nlog n))构建出(rk)数组就可以了。
我们倍增考虑的长度(k)。当(k>=n)时退出。
- 每次倍增开始前,你要保证这些后缀的排名是将他们“长度为(k)的前缀”排序后的排名。若两个字符串“长度为(k)的前缀”相同,则排名也应相同。
- 然后把每个后缀(i)“以(rk[i])为第一关键字、(rk[i+k])为第二关键字”再次排序,更新(rk)数组。(对于越界的(rk)下标,你认为其值为(-infty))这样你就得到了按“长度为(2k)的前缀“排序后的排名辣!
最开始时把所有后缀按它们的首字母排序,获得初始排名(rk)数组即可。
排序过程采用基数排序,则总复杂度为(O(nlog n))。
举个栗子:
b a n a n a
2 1 3 1 3 1 // k=1
3 2 4 2 4 1 // k=2
3 2 5 2 4 1 // k=4
4 3 6 2 5 1 // k=8
用途 - LCP(最长公共前缀)
我们令(lcp(s_1, s_2))为字符串(s_1, s_2)的最长公共前缀。令(LCP(i,j)=lcp(s[sa[i],n], s[sa[j],n]))。
那么我们有引理(LCP-Lemma):(forall i<j<k, LCP(i,j)=min(LCP(i,j),LCP(j,k)))
这样我们就得到了推论:(LCP(i,j)=sumlimits_{k=i+1}^j LCP(k-1,k)),原问题转为(RMQ)。但不幸的是,(LCP(k-1,k))你还只会(O(n^2))预处理,这显然是不行的。
height 数组
我们设(height[i]=LCP(i,i-1), h[i]=height[rk[i]]),则(height[1]=0)。考虑如何高效求(h)数组。
有一个结论(LCP-Theorem):(h[i]ge h[i-1]-1)
根据(LCP-Theorem),每次(h[i])直接从(h[i-1]-1)开始大力枚举就好。复杂度(O(n))。
再举一个栗子:
s: b a n a n a
h: 0 3 2 1 0 0
a -> 0
ana -> 1
anana -> 3
banana -> 0
na -> 0
nana -> 2
引理及证明
引理LCP-Lemma
(forall i<j<k, LCP(i,j)=min(LCP(i,j),LCP(j,k)))
证明:设(p=min(LCP(i,j),LCP(j,k))),则我们显然有(pge LCP(i,k))。
结合(i<j<k),我们可以知道:(s[sa[i]+p]<s[sa[j]+p])和(s[sa[j]+p]<s[sa[k]+p])中至少一个成立。否则(p<min(LCP(i,j),LCP(j,k)))并产生矛盾。
所以(s[sa[i]+p]<s[sa[k]+p]),故(ple LCP(i,k))。证毕。
定理LCP-Theorem
(h[i]ge h[i-1]-1)
证明:当(h[i-1]=0)时,显然成立。下面讨论(h[i-1]>0)的情况。
设(k=sa[rk[i-1]-1]),则(h[i-1]=lcp(s[k,n], s[i-1,n])),那么有(left{egin{array}{**lr**} h[i-1]-1=lcp(s[k+1,n], s[i,n])\ rk[k+1]<rk[i] end{array} ight.)(这一步相当于把他们的首字符砍掉)。
结合(LCP-Lemma),可得
即(lcp(s[k+1,n], s[i,n])le LCP(rk[i]-1, rk[i]))
即(h[i-1]-1le h[i]),证毕。
以上是关于后缀数组的主要内容,如果未能解决你的问题,请参考以下文章