• 后缀数组(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
  • 相关阅读:
    企业组织与经营管理
    VS2008试用版到期解决办法
    [分期付款] 建行安居分期,刚申请过,最高额度居然能申请到100万!汗。。
    [手游新项目历程]-29- windows-PC与 linux虚拟机交互
    诛仙手游培元属性
    [Warning] TIMESTAMP with implicit DEFAULT value
    bzoj3631
    bzoj1257
    bzoj1041
    bzoj1560
  • 原文地址:https://www.cnblogs.com/x1957/p/3109271.html
Copyright © 2020-2023  润新知