use std::cmp::{max, min};
pub fn compute_mat_ih_iv<T>(a: &[T], b: &[T]) -> (Vec<Vec<usize>>, Vec<Vec<usize>>)
where
T: Eq,
{
let na = a.len();
let nb = b.len();
let mut ih = vec![vec![0; nb + 1]; na + 1];
let mut iv = vec![vec![0; nb + 1]; na + 1];
for j in 0..(nb + 1) {
ih[0][j] = j;
}
for l in 0..(na + 1) {
iv[l][0] = 0;
}
for l in 1..(na + 1) {
for j in 1..(nb + 1) {
if a[l - 1] != b[j - 1] {
ih[l][j] = max(iv[l][j - 1], ih[l - 1][j]);
iv[l][j] = min(iv[l][j - 1], ih[l - 1][j]);
} else {
ih[l][j] = iv[l][j - 1];
iv[l][j] = ih[l - 1][j];
}
}
}
(ih, iv)
}
pub fn compute_vec_ig<T>(a: &[T], b: &[T]) -> Vec<usize>
where
T: Eq,
{
let na = a.len();
let nb = b.len();
let mut ih = vec![vec![0; nb + 1], vec![0; nb + 1]];
let mut iv = vec![vec![0; nb + 1], vec![0; nb + 1]];
for j in 0..(nb + 1) {
ih[0][j] = j;
}
for l in 1..(na + 1) {
iv[1][0] = 0;
for j in 1..(nb + 1) {
if a[l - 1] != b[j - 1] {
ih[1][j] = max(iv[1][j - 1], ih[0][j]);
iv[1][j] = min(iv[1][j - 1], ih[0][j]);
} else {
ih[1][j] = iv[1][j - 1];
iv[1][j] = ih[0][j];
}
}
ih.swap(0, 1);
iv.swap(0, 1);
}
ih.into_iter().next().unwrap()
}
pub fn compute_vg_dg_from_ig<T>(
a: &[T],
b: &[T],
ig: &Vec<usize>,
) -> (Vec<Option<usize>>, Vec<Option<usize>>)
where
T: Eq,
{
let na = a.len();
let nb = b.len();
let mut vg = vec![None; nb + 1];
let mut dg = vec![Some(0); na + 1];
let mut i = 1;
for j in 1..(nb + 1) {
if ig[j] == 0 {
dg[i] = Some(j);
i += 1;
} else {
vg[ig[j]] = Some(j);
}
}
for l in i..(na + 1) {
dg[l] = None;
}
(vg, dg)
}
pub fn compute_ig_vg_dg_from_ih_mat<T>(
a: &[T],
b: &[T],
ih: &Vec<Vec<usize>>,
) -> (Vec<usize>, Vec<Option<usize>>, Vec<Option<usize>>)
where
T: Eq,
{
let ig = ih[ih.len() - 1].clone();
let (vg, dg) = compute_vg_dg_from_ig(a, b, &ih[ih.len() - 1]);
(ig, vg, dg)
}
pub fn alcs<T>(a: &[T], b: &[T]) -> (Vec<usize>, Vec<Option<usize>>, Vec<Option<usize>>)
where
T: Eq,
{
let ig = compute_vec_ig(a, b);
let (vg, dg) = compute_vg_dg_from_ig(a, b, &ig);
(ig, vg, dg)
}
pub fn alcs_mat<T>(
a: &[T],
b: &[T],
) -> (
Vec<Vec<usize>>,
Vec<Vec<usize>>,
Vec<usize>,
Vec<Option<usize>>,
Vec<Option<usize>>,
)
where
T: Eq,
{
let (ih, iv) = compute_mat_ih_iv(a, b);
let (ig, vg, dg) = compute_ig_vg_dg_from_ih_mat(a, b, &ih);
(ih, iv, ig, vg, dg)
}
pub struct Alcs {
ig: Vec<usize>,
}
impl Alcs {
pub fn new<T>(a: &[T], b: &[T]) -> Self
where
T: Eq,
{
Alcs {
ig: compute_vec_ig(a, b),
}
}
pub fn suffix(&self, pos: usize) -> AlcsIterator {
AlcsIterator::new(&self, pos)
}
}
pub struct AlcsIterator<'a> {
alcs: &'a Alcs,
i: usize,
j: usize,
prev: usize,
}
impl<'a> AlcsIterator<'a> {
fn new(alcs: &'a Alcs, pos: usize) -> Self {
AlcsIterator {
alcs,
i: pos,
j: pos + 1,
prev: 0,
}
}
}
impl<'a> Iterator for AlcsIterator<'a> {
type Item = (usize, usize, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.j >= self.alcs.ig.len() {
return None;
}
let cur = self.prev + (self.alcs.ig[self.j] <= self.i) as usize;
self.prev = cur;
self.j += 1;
Some((self.i, self.j - 1, cur))
}
}
fn score(b: &str, a: &str, tsh: Option<f32>) -> (f32, usize, usize) {
let va = a.chars().collect::<Vec<char>>();
let vb = b.chars().collect::<Vec<char>>();
let alcs = Alcs::new(&va, &vb);
let na = a.len();
let nb = b.len();
let many = match tsh {
None => nb,
Some(tsh) => (na as f32 / tsh) as usize,
};
let mut best = (0., 0, 0);
for i in 0..nb {
let mut maxrow = (0., 0, 0);
for (i, j, cij) in alcs.suffix(i).take(many) {
let cur = cij as f32 / max(j - i, na) as f32;
if cur > maxrow.0 {
maxrow = (cur, i, j);
}
}
if maxrow >= best {
best = maxrow;
}
}
best
}
pub trait FuzzyStrstr<T: AsRef<str>>: AsRef<str> {
fn fuzzy_find_pos(&self, s: T, tsh: f32) -> Option<(f32, usize, usize)> {
let s = score(self.as_ref(), s.as_ref(), Some(tsh));
if s.0 > tsh {
Some(s)
} else {
None
}
}
fn fuzzy_find_str<'a>(&'a self, s: T, tsh: f32) -> Option<(f32, &'a str)> {
let r = self.fuzzy_find_pos(s, tsh);
r.map(|(tsh, start, end)| (tsh, &self.as_ref()[start..end]))
}
fn fuzzy_contains(&self, s: T, tsh: f32) -> bool {
self.fuzzy_find_pos(s, tsh).is_some()
}
}
impl<S, T> FuzzyStrstr<T> for S
where
S: AsRef<str>,
T: AsRef<str>,
{
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}