[zz终于搞懂]后缀数组-建数组代码分析

至于是不是真的搞懂还不知道
话说看那个说五分钟搞懂的(http://blog.csdn.net/yxuanwkeith/article/details/50636898)我这个zz看了一个多小时
在知道这个东西之后已经过了3个月了
提高知识水平!

算法思想略(都知道了吧)
这里写图片描述-图

然而代码是天书啊!!!!!!!

void build_sa(int m) {
    int i, *x = t, *y = t2;
    for ( i = 0; i < m; i++)c[i] = 0;
    for ( i = 0; i < n; i++)c[x[i] = s[i]]++;
    for ( i = 1; i < m; i++)c[i] += c[i - 1];
    for (i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
    for (int k = 1; k <= n; k <<= 1){
        int p = 0;
        for (i = n - k; i < n; i++) y[p++] = i;
        for (i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k;
        for (i = 0; i < m; i++) c[i] = 0;
        for (i = 0; i < n; i++) c[x[y[i]]]++;
        for (i = 0; i < m; i++) c[i] += c[i - 1];
        for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x, y);
        p = 1;  x[sa[0]] = 0;
        for (i = 1; i < n; i++)
            x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
        if (p >= n) break;
        m = p;
    }
}

先看第一段

    for ( i = 0; i < m; i++) c[i] = 0;
    for ( i = 0; i < n; i++) c[x[i] = s[i]]++;
    for ( i = 1; i < m; i++) c[i] += c[i - 1];
    for ( i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;

带入aabaaaab进去,我们惊奇的发现,sa一周目是12456738,这不是把a和b分开列了吗?(即一位字符的排序)
二周目是14562783,啊,对了,这就是两位字符的排序。(下次就是四位)
我们推断出sa存的是字符串值从小到大的序列
怎么求的呢,先分析一周目(后面再讲后面的),先求前缀和,然后不同的字母就分到不同的阶层了(类似于基数排序)再依次减减覆盖掉所有点就行了
基数排序原理:一段数:17,21,57,56,58,67,82,92,93
可以先按个位存储,得到:21,82,92,93,56,17,57,67,58
看起来是更乱了,不过个位变得有序了。
再按十位排,得:17,21,56,57,58,67,82,92,93 这就是正解了。
时间复杂度是O(kn),k是位数。

第二段

大循环

for (int k = 1; k <= n; k <<= 1){
        。。。。
    }

k是倍增,都懂了。

第二段分讲

第一节
int p = 0;
        for (i = n - k; i < n; i++) y[p++] = i;
        for (i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k;

y数组存的是什么?以i为起始点长为k/2的第二关键字(即前进k格并长为k/2的字符的序)的排序
没有第二关键字的第二关键字排序一定在最前面,例:
一周目: y:81345627
与sa比较:12456738,除了第一个是8,其他都是减一后所得

第二节
        for (i = 0; i < m; i++) c[i] = 0;
        for (i = 0; i < n; i++) c[x[y[i]]]++;
        for (i = 0; i < m; i++) c[i] += c[i - 1];
        for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];

与前文基数排序十分相似,但是,这里把i改成了y[i],y[i]中存的是第二关键字的排序,就像是前面讲基数排序的个位数的排序,我们的第一关键字是什么?x[i]!而一周目时,它存的就是单个字符值(虽然后面会改变,并是翻倍改变)

第三节
        swap(x, y);
        p = 1;  x[sa[0]] = 0;
        for (i = 1; i < n; i++)
            x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
        if (p >= n) break;
        m = p;

这里的y数组就是老的x数组,p是最大排名,就是用老的x数组求新的x数组(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k])其实就是比较老数组是否一样,一样,则p不变,否则更新最大排名,直到p=n即所有排名都有了,sa数组就已经求完了,很容易由sa求rank(其实就是x)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值