大规模数据去重——后缀数组

前言

一开始从李沐那里听到的这个去重算法,记得是说把所有数据拼成一个长度为n的长字符串,然后从后往前切,形成n个后缀字符串,再字典排序,只要有两个断点处有部分重复的子串,它们在字典排序后挨得就近,就容易找到重复数据。
实际的后缀排序方法为了降低复杂度做了很多优化。
有倍增算法+基数排序,即先排序长度为1的字符块,然后基于它再排序长度为2的字符块,再是长度为4…并且如果长度不够,就在排序上给一个绝对领先的位置——0.
上面的方法还算好理解,另一个叫做诱导排序(Suffix Array Induced Sorting)的方法很难理解,缩写是SA-IS,是中山大学农革教授组于2009年发表的。2015年又发表了一篇对该算法实现的改进。它是目前求后缀数组最快的方法。太难了,有机会再学。

倍增算法+基数排序

对于字符串 a b c a a b abcaab abcaab
它的后缀有
a b c a a b , b c a a b , c a a b , a a b , a b , b abcaab,bcaab,caab,aab,ab,b abcaab,bcaab,caab,aab,ab,b
排序后,有
a b , a a b , a b c a a b , b , b c a a b , c a a b ab,aab,abcaab,b,bcaab,caab ab,aab,abcaab,b,bcaab,caab
设置i为后缀排名,即abcaab对应的i是1,aab对应2。

定义以下函数
s a ( i ) : sa(i): sa(i):后缀排序后,第i位的后缀在排序前所在的位置。例如aab对应的i为2,则 s a ( 2 ) = 4 sa(2)=4 sa(2)=4,说明aab在上面切好的后缀的第4位。

r a n k ( j ) : rank(j): rank(j):刚切完的第j个后缀序列(未排序)在排序后所在的排名。例如abb对应j为4,则 r a n k ( 4 ) = 2 rank(4)=2 rank(4)=2

显然,我们切的后缀子串不可能有完全重复的项,因为至少长度上不相同。那么,每个后缀子串有唯一的切分后/排序前序号,也就有唯一的排序后序号,这形成了一个双射。总之,如果一个后缀子串对应某一对 ( i , j ) (i,j) (i,j),则有 s a ( r a n k ( j ) ) = j sa(rank(j))=j sa(rank(j))=j r a n k ( s a ( i ) ) = i rank(sa(i))=i rank(sa(i))=i

算法如下:

  1. 以aabaaaab为例,第1次排序仅排序a,a,b,a,a,a,a,b。如果重复了,排序就相同。如图所示,比较简单,是简单的rank。
  2. 然后考虑排序aa,ab,ba,aa,aa,aa,ab,bx。x代表缺位,排序时缺位总是排在前面,因为它比其他的更短。能看到,这次切分是从每个token往后多加一个,实际上是长度*2,以利用到刚刚得到的rank值。此时每个token位得到两个rank值,基于此rank值再排序得到新rank。
  3. 可以发现,这是在分治地得到长度为 2 n 2^n 2n的字符串的排序。经此迭代,当 2 n 2^n 2n比当前字符串长度更大时,我们就已经得到了所有后缀字符串的排序。例如下图里,第4次排序得到长度为8的字符串的排序,就是aabaaaab,abaaaabx,baaaabxx,aaaabxxx,aaabxxxx,aabxxxxx,abxxxxxx,bxxxxxxx的排序,而在我们“x排最前面”的规则,且排序过程中遇不到同时有俩都是x的时候,所以我们的排序策略可以顺利进行,且和“长短不一字符串排序”的结果一样。

偷的图
偷的图,来源这里

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值