后缀数组(1)

后缀数组真心是强有力的字符串处理工具,有兴趣的可以参考下其他资料,比如集训队论文什么的。

我这里只是总结下我这两天学习的。

顾名思义,后缀数组,就是字符串后缀组成的数组。

比如abcde

就有后缀

abcde

bcde

cde

de

e

将这些后缀按字典序排序后组成的数组就叫后缀数组

设SA是后缀数组,对于bace的后缀

bace

ace

ce

e

按字典序排序未ace,bace,ce,e

那么SA[1]=ace,SA[2]=bace...

当然也不用记录一个字符串,用这个后缀在原组数的位置就可以表示这个后缀了

SA[1]=2,SA[2]=1...

然后还有个Rank数组,和SA正好相反

Rank[i]表示i后缀的排名

比如Rank[ace]=1,即Rand[2]=1

其实Rank就是SA的逆,Rank[SA[i]]=i

废话这么多,我们有这么一个数组有啥好处呢?

当然是为了利用这个数组的特性来做一些处理啦。

 ----------分割线-----------

既然SA这么有用,该怎么球呢,现在有两种方法

倍增,DC3

前者nlogn的复杂度,后者n的复杂度

具体怎么做,可以查阅其他资料(逃

-----------继续分割----------

后缀数组可以做很多神奇的事情。。。

比如呢,一个字符串,求最长重复字串(字串可重叠)

要是不会后缀数组,我一看,就会晕掉= =,尼玛好麻烦,要枚举每个字串的节奏么。

先引入一个概念height数组

height[i]=lcp(SA[i-1],SA[i]),lcp表示最长公共前缀

设h[i] = height[rank[i]],h数组有个很好用的性质

h[i] >= h[i-1] -1

这样我们就能用O(n)的时间求出height数组了

来证明下

首先是显而易见的

lcp(A_SA[x] , A_SA[z]) = min_x<y<=z{lcp(A_SA[y-1] , A_SA[y])}

简单的说就是rank x到z之间的lcp是之间每个相邻的lcp的最小值,这个不用说也知道是对的嘛

那我们就得到

Fact 1. lcp(A_SA[y-1] , A_SA[y]) >= lcp(A_SA[x] , A_SA[z]) x<y<=z

就是说在排名x,z字符串之间的字符串的lcp都大于等于lcp(x,z),这很显然

Fact 2. 如果lcp(A_SA[x-1] , A_SA[x]) > 1 那么 Rank[SA[x-1]+1] < Rank[SA[x]+1]

也是很明显的结论,因为这两个字符串有公共前缀,那么这个相同的前缀并不影响他们排名,即取消这个字符rank也是不变的

Fact 3. 如果lcp(A_SA[x-1] , A_SA[x]) > 1 那么lcp(A_SA[x-1]+1 , A_SA[x]+1) = lcp(A_SA[x-1] , A_SA[x]) - 1

继续使用显然这个词,这两个x-1,x有公共前缀,那么向后移动一位的字符串的lcp当然只他们没有移动时候-1啦

下面给出一个lemma

先定义

p=rank[i-1],q=rank[i]

j-1=SA[p-1],k=SA[q-1]

lemma 如果lcp(A_j-1,A_i-1) > 1 那么 lcp(A_k,A_i)>=lcp(A_j,A_i)

证明:

lcp(A_j-1,A_i-1)>1 => rank[j] < rank[i] ,by Fact 2

先把i,j,p,q,k什么的属性解释清楚吧

p,q是rank,p是i位置后缀的rank,q是i-1位置的后缀的rank

j-1是排在p前一位的后缀位置,k是排在q前一位的后缀位置

Fact2是说有两个后缀存在公共前缀,那么去掉一个字符后他们的rank也不变的

rank[j-1] = rank[sa[p-1]] = p-1

rank[i-1] = p 

所以本身rank[j-1] < rank[i-1]所以 rank[j] < rank[i]

rank[k]=rank[SA[q-1]] =q-1=rank[i]-1

所以rank[j] <= rank[k] = rank[i]-1

所以 lcp(A_k,A_i)>=lcp(A_j,A_i),这就很明显了k,i在j,i之间,所以他们的lcp要大于等于j,i

ok,现在我们来证明

如果 height[p] = lcp(A_j-1,A_i-1) > 1 那么 height[q] = lcp(A_k,A_i) >= height[p] -1

lcp(A_k,A_i) >= lcp(A_j,A_i) = lcp(A_j-1,A_i-1) - 1

-----证明结束--------

其实写了那么多证明,感觉一点都不直观,我还是习惯直观一点的

那么设后缀k是i-1前面那么一个,那么h[i-1]就是lcp(k,i-1)啦

如果lcp(k,i-1)<=1,那么就是很显然的 h[i]>=h[i-1]-1了(因为-1是0了。。囧

若lcp(k,i-1)>1,那么呢我们可以得到lcp(k+1,i) = h[i-1]-1 

显然rank[k] < rank[i-1]那么rank[k+1] < rank[i]

显然k排在i-1前面,然后他们有公共前缀,去掉公共的前缀k+1还是排在i前面的

rank[k+1] ....rank[i]-1...rank[i] , lcp(i,k+1) <= lcp(rank[i]-1,i)

所以嘛h[i] >= h[i-1]-1 , 由Fact1得到

附球height的伪代码

for i = 1 to n
	rank[sa[i]] = i
h = 0
for i = 1 to n
	if rank[i] > 1
		j = sa[rank[i]-1]
		while a[i+h] == a[j+h]
			h = h + 1
		height[rank[i]] = h
		if h > 0
			h = h - 1 

  

by 1957
原文地址:https://www.cnblogs.com/x1957/p/3109271.html