“滑动历史最长匹配把编码端搜索具体化:在完整输入的后缀索引上维护会到期的活动秩,只比较两个活动字典序邻居即可求最长长度及见证。这个见证不保证距离最短;固定位费用最优解析再按距离与长度的价格档保…”
解码器已经知道“往回两格、复制七字节”,可以逐字节照做。编码器却得先找到这两个数。逐个距离反复比较会把同一段文字读很多遍。本页把历史位置放到另一种顺序中:按它们开始的后缀排序,再问当前后缀左右最近的两个合法邻居。关键是“合法”也会随窗口移动,不能拿整张后缀数组里的相邻项直接替代。
形式陈述
输出长度,还要输出一个可用来源
输入为完整不可变字节串
SuffixIndex(T).band(a,b,C) 为每个
若 None,即使历史集合并不空。它不承诺找最近来源,也不承诺枚举全部并列来源。普通窗口
这里沿用LZ77重叠回指:来源只要求开始于过去,不要求整段结束于
本页建立完整输入的离线索引。利用未来字节来比较后缀是编码端的工作,不意味着解码器能读取未来。接口也不是只保留
两个活动邻居足够
设
证明直接使用LCP数组的区间最小值等式。若活动秩
左侧更远的来源不可能比最近活动前驱更长。右侧同理:从当前秩到更远来源的区间包含到最近活动后继的区间,最小值只会不增。再统一截到
插入、到期与顺序统计
按
当右端小于左端时集合为空。初始没有历史;从
用Fenwick树维护这些0/1频率。prefix(r)统计小于当前秩的活动项数,记为 prefix(n)为总数
for p = 0 .. n-1:
insert rank[p-a] if p-a >= 0
erase rank[p-b-1] if p-b-1 >= 0
s = active.prefix(rank[p]); z = active.prefix(n)
neighbors = kth(s) if s>0, then kth(s+1) if s<z
best = 0; witness = None
for r in neighbors:
q = SA[r]
length = min(C, n-p, LCP_query(p,q))
if length > best: best = length; witness = q
output (best,witness)
严格大于才更新,故两侧正长度并列时保留前驱。这是确定性规则,不是“选最小距离”的规则。
直觉
把全部后缀想成按字典序排好的书。当前后缀和某本书共享很长开头,就意味着夹在它们之间的每本书也共享这个开头。于是同一侧最靠近当前后缀的合法书不会更差。
窗口负责决定哪些书有资格,LCP负责比较内容。一个位置从时间上过期,只把对应秩的计数减一;书架和相邻LCP不需要重排。反过来,只在输入位置上保留最近两个来源是不够的,因为时间邻居与字典序邻居是两种顺序。
例子与边界
同长度,不同距离费用
取 ABAABABA,位置从0开始,ABA。后缀数组及LCP为
在距离带 A、长度1;后继为 ABA、长度3。因此返回
然而 ABA 开头,长度同为3,距离只有2。它被 Match(3,5)花7bit,Match(3,2)只花5bit。最长长度正确,不等于复制来源已经最便宜。
把来源改限于距离带
重叠不是越界
对 ABABABABA、Lit(A), Lit(B), Match(7,2) 时,每次复制从当前输出末尾往回两格读取。第3个复制字节已经读到本匹配第1步写出的A。若错误加上“长度不得超过距离”,这个合法长匹配就会消失。
零、字节与表示边界
空串输出空列表。
输入可含全部256种字节,包括零字节与255。构造时把每个字节
推论与应用
预处理与实际费用
公开实现采用后缀数组的计数排序倍增构造:在带唯一终止符的循环串上按长度1、2、4……的秩对排序,每轮用已排序的后半部顺序和前半部计数排序完成线性工作,最后删除终止符后缀。随后用Kasai扫描构造前驱LCP。输入字母表固定为字节,预处理时间为
LCP数组静态存入线段树,两后缀的公共前缀通过相应区间最小值求得。它在本实现中每次花
trace=True另列出每个位置的完整活动集合,并逐项调用顺序统计。最坏额外时间为
若只要一个贪心解析,先求所有位置的答案,再按token跳过已消费位置。不能在跳跃时只做一次插入删除,否则活动集合会遗漏被跳过的历史。公开 greedy_parse正是先完整扫描,后读取解析起点。
文献中的KKP算法在完整历史模型上进一步利用特定PSV/NSV结构达到线性界;本页的到期距离带、Fenwick和树查询不是那段实现,不继承它的线性结论。[1] 更重要的后续是把距离费用分组:同一价格档只需一个最长见证,便能支持该档所有更短合法匹配;跨价格档仍须保留区别。
完整终点任务要求交出实际活动秩、到期动作、匹配见证和逐字节复制结果,再把这些证书用于位费用优化。
参考资料
- [1] Juha Kärkkäinen, Dominik Kempa and Simon J. Puglisi, Linear Time Lempel-Ziv Factorization: Simple, Fast, Small, CPM 2013, pp. 189–200,§2 的LPF与两个字典序候选,§§3–5 的线性实现。本文把候选证明用于指定活动子集,窗口更新和成本另外证明;不是照搬无到期历史的线性算法。
- Paolo Ferragina, Igor Nitto and Rossano Venturini, On the Bit-Complexity of Lempel-Ziv Compression, SIAM Journal on Computing 42(4), 2013, pp. 1521–1541,§§3、5.2:复制距离费用和历史窗口分组。
- L. Peter Deutsch, RFC1951, §3.2.3:重叠长度/距离复制的具体语义;本文没有生成DEFLATE块。