Skip to main content

lzcomplexity_core/
lpf.rs

1//! Longest Previous Factor — port of `lz::utils::LPF` from modules/core/src/lpf.cpp.
2//!
3//! For each position i in the sequence, lpf[i] is the length of the longest
4//! factor starting at i that also occurs earlier in the sequence. The C++
5//! implementation operates on the SA / LCP arrays via a stack of (len, pos).
6
7/// Compute the LPF array. `lpf` must already be sized to `n`.
8pub fn lpf(lpf_out: &mut [u32], mut sa: Vec<u32>, mut lcp_in: Vec<u32>, n: usize) {
9    debug_assert!(lpf_out.len() >= n);
10    if n == 0 {
11        return;
12    }
13    if n == 1 {
14        lpf_out[0] = 0;
15        return;
16    }
17
18    // The C++ pushes a 0 sentinel onto SA and LCP, indexing up to i == n.
19    sa.push(0);
20    lcp_in.push(0);
21
22    // Stack of (len, pos). pos here matches the C++ semantics: SA[i] + 1.
23    let mut stack: Vec<(u32, u32)> = Vec::new();
24    stack.push((0, sa[0] + 1));
25
26    for i in 1..=n {
27        let mut lcp = lcp_in[i];
28        while let Some(&(top_len, top_pos)) = stack.last() {
29            if sa[i] + 1 >= top_pos {
30                break;
31            }
32            // lpf[top_pos - 1] = max(top_len, lcp); lcp = min(top_len, lcp); stack.pop()
33            let target = (top_pos - 1) as usize;
34            if target < lpf_out.len() {
35                lpf_out[target] = top_len.max(lcp);
36            }
37            lcp = top_len.min(lcp);
38            stack.pop();
39        }
40        if i < n {
41            stack.push((lcp, sa[i] + 1));
42        }
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::suffix_array::{build_lcp, build_suffix_array};
50
51    #[test]
52    fn lpf_banana() {
53        let text = b"banana";
54        let sa = build_suffix_array(text);
55        let lcp = build_lcp(text, &sa);
56        let mut out = vec![0u32; text.len()];
57        lpf(&mut out, sa, lcp, text.len());
58        // Reasonable smoke test: the LZ76 walk over this must produce
59        // a non-empty factor list (validated indirectly in lz76 tests).
60        assert_eq!(out.len(), 6);
61    }
62}