后缀数组 学习笔记

后缀数组??!!

后缀数组是处理字符串的强有力的工具…..
在字符串处理当中,后缀树和后缀数组都是非常有力的工具。其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。可以说,在信息学竞赛中后缀数组比后缀树要更为实用。 − − − − 百度百科

后缀数组中变量的定义:

我们定义 suffix(i) s u f f i x ( i ) 为以i开始到结束的后缀.
SA[i] S A [ i ] :排第 i i 名的后缀的位置,排名是按照字典序来排的
Rank[i]: suffix(i) s u f f i x ( i ) 排第几
这时候我们就会发现 SA[]Rank[] S A [ ] 和 R a n k [ ] 互为逆运算,即 SA=Rank1 S A = R a n k − 1
c[] c [ ] :基数排序的数组
x[],y[] x [ ] , y [ ] :第一二关键字的排名
我们怎样构建后缀数组呢?
我们就是对所有的 suffix(i) s u f f i x ( i ) 进行排序,然后计算出 SA[]Rank[] S A [ ] 和 R a n k [ ]
要排序的话快排是 O(nlogn) O ( n l o g n ) ,我们可以用基数排序来 O(n) O ( n ) 的时间来进行排序

后缀数组的构建

一般两种方法 D3 D 3 和倍增
倍增相对来说比较好写,所以我们这里介绍倍增算法
我们就是先选定当前 i i 的排名作为第一关键字,然后往后第2k个字符的排名作为第二个关键字,直到 2k>n 2 k > n
后缀数组的奥秘就隐藏在下面这一张图中
假如我们现在要构建 aabab a a b a b 的后缀数组
下面是倍增求排名的过程
这里写图片描述
下面是 SARank S A 和 R a n k 数组
这里写图片描述

代码

后缀数组的构建难理解的就是基数排序;

void Build_SA()
{
    int n=Len,m=150;
    for (int i=0;i<m;i++)   c[i]=0;
    for (int i=0;i<n;i++)   c[x[i]=s[i]]++;
    for (int i=1;i<m;i++)   c[i]+=c[i-1];
    for (int i=n-1;i>=0;i--) SA[--c[x[i]]]=i;
    //先处理出单个字符的排名
    for (int k=1;k<=n;k<<=1)//倍增搞一搞
    {
        int p=0;
        for (int i=n-k;i<n;i++) y[p++]=i;//第二关键字的排序
        for (int i=0;i<n;i++)   if (SA[i]>=k) y[p++]=SA[i]-k;

        for (int i=0;i<m;i++)   c[i]=0;
        for (int i=0;i<n;i++)   c[x[i]]++;
        for (int i=1;i<m;i++)   c[i]+=c[i-1];
        for (int i=n-1;i>=0;i--)SA[--c[x[y[i]]]]=y[i];//第一二关键字一起排 
        swap(x,y);
        p=1; x[SA[0]]=0;
        for (int i=1;i<n;i++)
            x[SA[i]]=y[SA[i-1]]==y[SA[i]]&&((SA[i-1]+k>=n?-1:y[SA[i-1]+k])==(SA[i]+k>=n?-1:y[SA[i]+k]))?p-1:p++;//下一次排序继续用,更新SA[]
        if (p>n) break;
        m=p;
    }   
}

Height数组

后缀数组比较有名还源于 Height H e i g h t 数组
Height H e i g h t 数组的求解可以做到 O(n) O ( n )
这要基于 Height[] H e i g h t [ ] 的一个性质
Height[i]>=Height[i1]1 H e i g h t [ i ] >= H e i g h t [ i − 1 ] − 1
这里的 i i <script type="math/tex" id="MathJax-Element-1543">i</script>指的是排名

void Built_Height()
 {
    for (int i=0;i<n;++i) Rank[SA[i]]=i;//利用逆运算的性质求出Rank
    int k=0;Height[0]=0;
    for (int i=0;i<n;++i)
    {
        if (!Rank[i]) continue;
        if (k) --k;
        int j=SA[Rank[i]-1];
        while (i+k<n&&j+k<n&&s[i+k]==s[j+k]) 
            ++k;//根据性质来求
        Height[Rank[i]]=k;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值