后缀数组 详解(1)

本文介绍了使用SA(SuffixArray)算法对字符串进行排序,通过字符间的比较转换为数字比较,以及利用倍增思想和字符串匹配概念优化字符子串比较过程,以降低复杂度。同时讨论了将字符编码为不同进制进行排序的局限性。
摘要由CSDN通过智能技术生成

int sa[105], rk[105];

bool cmp(int a, int b)

{

return S.substr(a) < S.substr(b);

}

void construct_sa()

{

for(int i = 0; i <= S.length(); i++)

{

sa[i] = i;

}

sort(sa, sa + S.length() + 1, cmp);

rk[sa[0]] = 0;

for(int i = 1; i <= S.length(); i++)

{

rk[sa[i]] = rk[sa[i-1]] + 1;

}

}

字符串 S 为 abeacadabea

sa: 11 10 7 0 3 5 8 1 4 6 9 2

rk: 3 7 11 4 8 5 9 2 6 10 1 0

② 方法

===================================================================

字符之间的比较所消耗的复杂度为 O( n ),而数字之间的比较复杂度为 O( 1 ),如果我们能将字符之间比较转化为数字之间比较,是否可以降低复杂度呢

思路:我们用倍增思想,先比较长度为 2 的子串,再利用该结果比较长度为 4 的子串,再利用该结果比较长度为 8 的子串 … ( 长度为 1 的子串不用比较,因为自身的 ASCII 值就已经相当于比较了)

( 如果 剩余字符 不足 长度 2^k ,则表示到 字符串S的末尾 )

我们应该怎么利用上次比较的结果呢,比如长度为 k 的子串我们已经比较好了,那么长度为 2 * k 的子串,我们就只用看 i 开头长度为 k 的子串排序序号,和 i + k 开头长度为 k 的子串排序序号,然后比较序号之间就行,这样就把字符串之间的比较,转换成了数字比较

复杂度为 O( n * log n * log n )

代码如下:

string S;

int sa[105], rk[105], tmp[105];

int k;

bool cmp(int a, int b)

{

if(rk[a] == rk[b])

{

int i = a + k <= S.length() ? rk[a + k] : -1;

int j = b + k <= S.length() ? rk[b + k] : -1;

return i < j;

}

return rk[a] < rk[b];

}

void construct_sa()

{

for(int i = 0; i <= S.length(); i++)

{

sa[i] = i;

rk[i] = i < S.length() ? S[i] - ‘a’ + 1 : 0;

}

for(int i = 1; i <= S.length(); i = i * 2)

{

k = i;

sort(sa, sa + S.length() + 1, cmp);

tmp[sa[0]] = 0;

for(int i = 1; i <= S.length(); i++)

{

tmp[sa[i]] = tmp[sa[i-1]] + (cmp(sa[i-1], sa[i]) ? 1 : 0);

}

for(int i = 0; i <= S.length(); i++)

{

rk[i] = tmp[i];

}

}

}

字符串 S 为 abeacadabea

sa: 11 10 7 0 3 5 8 1 4 6 9 2

rk: 3 7 11 4 8 5 9 2 6 10 1 0

感觉注释写代码里看不清(注释颜色太浅),我就在这解释下:

  1. tmp 是一个临时的数组,因为如果我们直接更新 rk 结果数组,这样我们在判断长度为 k 的字符子串时,可能会有已经更新过的 长度为 2k 的字符子串混入,会影响判断结果

  2. 我们对于每次的排序都需要再处理一次,因为当两个字符子串大小相同时,我们需要让排序序号相同,不然会影响下次的判断

③ 方法

===================================================================

我们是否可以用字符串匹配的思想,把每个后缀列用数字表达出来,然后排个序

例:字符串 S 为 abeacadabea 时,部分后缀为:

11个空字符

a + 10 个空字符

ea + 9 个空字符

bea + 8 个空字符

abea + 7 个空字符

如果给每种字符编上号,例如 空字符 等于 0,a 等于 1,b 等于 2 …

那我们只用比较这几个数的大小就可以了( 当最大种数小于 10 的时候 )

00000000000

10000000000

51000000000

25100000000

12510000000

但这种十进制的表达只有字符最大种数小于等于 10 的情况才能用,那如果情况不为 10 种的情况,我们可以将 十进制 改为 n进制 就行,这样每一位仍然可以表达字符是什么

但是这个很不靠谱,因为我们比较的是大小,所以我们不能取模,这就导致很容易就出现一个巨大无比的数,很容易就无法判断了

复杂度为 O( n * logn ),但也就数据很小很小的时候可以用用,大多数没用

代码如下:

const ll B = 10;

string S;

pair<ll, int> sa[105];

int rk[105];

int k;

void construct_sa()

{

ll T = 1;

for(int i = 0; i < S.length(); i++)

{

T = T * B;

}

ll t = 0;

sa[S.length()] = make_pair(t, S.length());

for(int i = S.length() - 1; i >= 0; i–)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可获取,包括答案解析。

持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可获取,包括答案解析。

[外链图片转存中…(img-CKNeNhIU-1713204794561)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值