diff --git a/src/lib.rs b/src/lib.rs index b76a9b4..3126c64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ -pub mod sequencematcher; \ No newline at end of file +pub mod sequencematcher; +mod utils; \ No newline at end of file diff --git a/src/sequencematcher.rs b/src/sequencematcher.rs index 8cafd08..93cdca4 100644 --- a/src/sequencematcher.rs +++ b/src/sequencematcher.rs @@ -1,13 +1,38 @@ -#[derive(Debug)] +use std::cmp::Ordering; +use utils::slice_str; + +#[derive(Debug, Clone, Copy)] pub struct Match{ first_start: usize, second_start: usize, size: usize } +impl Ord for Match { + fn cmp(&self, other: &Match) -> Ordering { + self.size.cmp(&other.size) + } +} + +impl PartialOrd for Match { + fn partial_cmp(&self, other: &Match) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for Match { + fn eq(&self, other: &Match) -> bool { + self.size == other.size + } +} + +impl Eq for Match { } + + pub struct SequenceMatcher<'a>{ first_sequence: &'a str, - second_sequence: &'a str + second_sequence: &'a str, + matching_blocks: Option> } impl<'a> SequenceMatcher<'a>{ @@ -15,38 +40,83 @@ impl<'a> SequenceMatcher<'a>{ { SequenceMatcher{ first_sequence: first_sequence, - second_sequence: second_sequence + second_sequence: second_sequence, + matching_blocks: None } } - pub fn find_longest_match(&self, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Match - { - let first_sequence: Vec = self.first_sequence.chars().collect(); - let second_sequence: Vec = self.second_sequence.chars().collect(); - let mut start = 0; - let mut end = 0; + pub fn find_longest_match(&self, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Option + { + let first_sequence: Vec = slice_str(self.first_sequence, first_start, first_end).unwrap_or("").chars().collect(); + let second_sequence: Vec = slice_str(self.second_sequence, second_start, second_end).unwrap_or("").chars().collect(); + let mut max_i = 0; + let mut max_j = 0; let mut arr: Vec> = vec![vec![0; second_sequence.len() + 1]; first_sequence.len() + 1]; for i in 0..first_sequence.len(){ for j in 0..second_sequence.len(){ if first_sequence[i] == second_sequence[j]{ arr[i + 1][j + 1] = arr[i][j] + 1; - if arr[i + 1][j + 1] > arr[start][end]{ - start = i + 1; - end = j + 1; - } + if arr[i + 1][j + 1] > arr[max_i][max_j] { + max_i = i + 1; + max_j = j + 1; + } } } } - Match{ - first_start: start - arr[start][end], - second_start: end - arr[start][end], - size: arr[start][end] + if max_i == 0 && max_j == 0{ + return None + } + else{ + let size = arr[max_i][max_j]; + Some(Match{ + first_start: max_i - size + first_start, + second_start: max_j - size + second_start, + size: size + }) } } - pub fn get_matching_blocks(&self) //-> Vec + pub fn get_matching_blocks(&mut self) -> Vec { - let first_length = self.first_sequence.len(); - let second_length = self.second_sequence.len(); + if self.matching_blocks.as_ref().is_some(){ + return self.matching_blocks.as_ref().unwrap().clone() + } + let (first_length, second_length) = (self.first_sequence.len(), self.second_sequence.len()); + let mut matches = Vec::new(); + let mut queue = vec![(0, first_length, 0, second_length)]; + while !queue.is_empty(){ + let (first_start, first_end, second_start, second_end) = queue.pop().unwrap(); + let m = self.find_longest_match(first_start, first_end, second_start, second_end); + match m { + Some(m) => { + if first_start < m.first_start && second_start < m.second_start{ + queue.push((first_start, m.first_start, second_start, m.second_start)); + } + if m.first_start + m.size < first_end && m.second_start + m.size < second_end{ + queue.push((m.first_start + m.size, first_end, m.second_start + m.size, second_end)); + } + matches.push(m); + }, + None => {}, + } + } + matches.sort_by(|a, b| a.cmp(b)); + let (mut first_start, mut second_start, mut size) = (0, 0, 0); + let mut non_adjacent = Vec::new(); + for m in &matches{ + if first_start + size == m.first_start && second_start + size == m.second_start{ + size += m.size + } + else { + if size != 0{ + non_adjacent.push(m.clone()); + } + first_start = m.first_start; + second_start = m.second_start; + size = m.size; + } + } + self.matching_blocks = Some(non_adjacent); + self.matching_blocks.as_ref().unwrap().clone() } } \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..4066c42 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,12 @@ +pub fn slice_str(string: &str, start: usize, end: usize) -> Option<&str> +{ + if start >= end{ + return None + } + if start > string.len() || end > string.len(){ + return None + } + unsafe{ + Some(string.slice_unchecked(start, end)) + } +} \ No newline at end of file