use crate::{Algorithm, Result};
use alloc::vec;
use alloc::vec::Vec;
#[derive(Default)]
pub struct LCSSeq {}
impl Algorithm<usize> for LCSSeq {
fn for_vec<E: Eq>(&self, s1: &[E], s2: &[E]) -> Result<usize> {
let l1 = s1.len();
let l2 = s2.len();
let mut lengths = vec![vec![0; l2 + 1]; l1 + 1];
for (i, char1) in s1.iter().enumerate() {
for (j, char2) in s2.iter().enumerate() {
lengths[i + 1][j + 1] = if char1 == char2 {
lengths[i][j] + 1
} else {
lengths[i + 1][j].max(lengths[i][j + 1])
};
}
}
let mut result = Vec::<&E>::new();
let mut i = l1;
let mut j = l2;
while i != 0 && j != 0 {
if lengths[i][j] == lengths[i - 1][j] {
i -= 1;
} else if lengths[i][j] == lengths[i][j - 1] {
j -= 1;
} else {
assert!(s1[i - 1] == s2[j - 1]);
result.push(&s1[i - 1]);
i -= 1;
j -= 1;
}
}
Result {
abs: result.len(),
is_distance: false,
max: l1.max(l2),
len1: l1,
len2: l2,
}
}
}
#[cfg(test)]
mod tests {
use crate::str::lcsseq;
use assert2::assert;
use proptest::prelude::*;
use rstest::rstest;
#[rstest]
#[case("", "", 0)]
#[case("", "abcd", 0)]
#[case("abcd", "", 0)]
#[case("ab", "cd", 0)]
#[case("abcd", "abcd", 4)] #[case("test", "text", 3)] #[case("thisisatest", "testing123testing", 7)] #[case("abcd", "c", 1)] #[case("abcd", "d", 1)] #[case("abcd", "e", 0)] #[case("abcdefghi", "acegi", 5)] #[case("abcdgh", "aedfhr", 3)] #[case("aggtab", "gxtxayb", 4)] #[case("你好,世界", "再见世界", 2)] fn function_str(#[case] s1: &str, #[case] s2: &str, #[case] exp: usize) {
assert!(lcsseq(s1, s2) == exp);
}
proptest! {
#[test]
fn prop(s1 in ".*", s2 in ".*") {
let res = lcsseq(&s1, &s2);
let res2 = lcsseq(&s2, &s1);
prop_assert_eq!(res, res2);
prop_assert!(res <= s1.len() || res <= s2.len());
}
}
}