匹配LZ77 / LZSS上的重叠前瞻与后缀树

时间:2015-07-10 18:13:58

标签: c++ algorithm suffix-tree lossless-compression lz77

后台:我在C ++上有一个通用LZSS后端的实现(可用here。我在这个版本中使用的匹配算法非常简单,因为它最初是为了压缩相对较小的文件(最多64kB)用于相对古老的硬件(特别是Mega Drive / Sega Genesis,其中64kB是整个主RAM)。

然而,有些文件需要很长时间来压缩我的实现,大约几分钟。原因有两个:天真匹配算法占用大部分时间,但这种情况特别是因为我从文件构造压缩图以实现最佳压缩。在分析器上查看,大部分时间都花在寻找匹配上,甚至使结果图的二次大小相形见绌。

有一段时间,我一直在研究几种潜在的替代品;引起我注意的是dictionary-symbolwise flexible parsing using multilayer suffix trees。多层部分很重要,因为我感兴趣的LZSS变体之一使用可变大小编码(位置,长度)。

我当前的实现允许滑动窗口中的匹配与前瞻缓冲区重叠,因此输入:

aaaaaaaaaaaaaaaa

可以直接编码为

(0,'a')(1,0,15)

而不是

(0,'a')(1,0,1)(1,0,2)(1,0,4)(1,0,8)

这里,(0,'a')表示将字符'a'编码为文字,而(1,n,m)表示'从位置n'复制m个字符。

问题:说了这么多,这就是我的问题:我在后缀树上找到的每一个资源似乎都暗示他们无法处理重叠的情况,而只是让你找到不重叠的比赛。当涉及后缀树时,研究论文,书籍甚至一些实现给出了压缩示例而没有重叠,好像它们是完美的压缩(我会链接到其中一些但我的声誉不允许它)。有些人甚至提到在描述基本压缩方案时重叠可能很有用,但在讨论后缀树时,这个问题很奇怪。

由于无论如何都需要扩充后缀树以存储偏移量信息,这看起来像是在查找匹配时可以检查的属性 - 您将过滤掉在前瞻缓冲区中开始的任何匹配。构造/更新树的方式意味着如果边缘将您带到与前瞻开始的匹配相对应的节点,则返回前一个节点,因为任何其他后代也将在前瞻中缓冲液中。

我的方法是错误还是不正确?是否有LZ77 / LZSS的实现或讨论,后缀树提到匹配重叠前瞻缓冲区?

1 个答案:

答案 0 :(得分:3)

根据我的理解,给定一个后缀树,我们感兴趣(大致)计算,对于每个后缀S,​​更长的后缀具有最长的共同前缀S。

从每个树节点向后代叶添加引用,后缀最长(使用DFS的线性时间)。从每个叶子,向后走,检查新的引用,如果找到更长的后缀,则停止。后一步的运行时间是线性的,因为每个树边缘只检查一次。

不幸的是,有界窗户的生活更加困难。我们传播了几个,而不是传播一个引用。要计算从节点引用的后缀集,我们按长度排序顺序合并它们。然后每当我们有长度x>的后缀时。 y&gt; z,如果x - z < 8192(例如),然后我们可以删除y,因为所有三个对于当前节点是最左边的共同祖先的所有后缀同样有用,并且如果y在窗口中,则x或z是。由于窗口是整个文件的很大一部分,因此每个节点最多只有少量引用。

如果你想回顾可能的最短距离,那么就有一个O(n log ^ 2 n)时间算法(可能是O(n log n)可以改进的,具有各种难以实现的魔法)。在算法过程中,我们为每个节点构造一个后代后缀长度的二叉搜索树,然后进行下一个最长的查找。要从其子节点构造节点的树,请从最大的子树开始,然后插入其他子元素。通过heavy path参数,每个长度插入O(log n)次。