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

来源:互联网 发布:51单片机串口通信程序 编辑:程序博客网 时间:2024/06/05 02:50

至于是不是真的搞懂还不知道
话说看那个说五分钟搞懂的(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 0
原创粉丝点击