use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct SubSequenceTrie {
root: TrieNode,
}
#[derive(Debug, Clone)]
struct TrieNode {
children: HashMap<String, TrieNode>,
}
impl TrieNode {
fn new() -> Self {
Self {
children: HashMap::new(),
}
}
}
impl SubSequenceTrie {
pub fn new() -> Self {
Self {
root: TrieNode::new(),
}
}
pub fn from_sequences(sequences: Vec<Vec<String>>) -> Self {
let mut trie = Self::new();
for sequence in sequences {
trie.insert_sequence(sequence);
}
trie
}
pub fn insert_sequence(&mut self, sequence: Vec<String>) {
let mut current_node = &mut self.root;
for item in sequence {
current_node = current_node
.children
.entry(item)
.or_insert_with(TrieNode::new);
}
}
pub fn contains<T>(&self, sequence: &[T]) -> bool
where
T: AsRef<str>,
{
self.search_subsequence_recursive(&self.root, sequence, 0)
}
fn search_subsequence_recursive<T>(
&self,
node: &TrieNode,
sequence: &[T],
seq_index: usize,
) -> bool
where
T: AsRef<str>,
{
if seq_index >= sequence.len() {
return true;
}
let target_element = sequence[seq_index].as_ref();
for (child_key, child_node) in &node.children {
if child_key == target_element {
if self.search_subsequence_recursive(child_node, sequence, seq_index + 1) {
return true;
}
} else {
if self.search_subsequence_recursive(child_node, sequence, seq_index) {
return true;
}
}
}
false
}
}
impl Default for SubSequenceTrie {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_trie_is_empty() {
let trie = SubSequenceTrie::new();
assert!(!trie.contains(&["A"]));
assert!(trie.contains(&[] as &[&str]));
}
#[test]
fn test_insert_single_sequence() {
let mut trie = SubSequenceTrie::new();
trie.insert_sequence(vec!["A".to_string(), "B".to_string(), "C".to_string()]);
assert!(trie.contains(&["A", "B", "C"]));
assert!(trie.contains(&["A"]));
assert!(trie.contains(&["A", "B"]));
assert!(trie.contains(&["A", "C"]));
assert!(trie.contains(&["B", "C"]));
assert!(!trie.contains(&["C", "A"]));
assert!(!trie.contains(&["B", "A"]));
assert!(!trie.contains(&["D"]));
}
#[test]
fn test_from_sequences() {
let sequences = vec![
vec!["A".to_string(), "B".to_string(), "C".to_string()],
vec!["A".to_string(), "D".to_string(), "E".to_string()],
vec!["X".to_string(), "Y".to_string()],
];
let trie = SubSequenceTrie::from_sequences(sequences);
assert!(trie.contains(&["A", "B"]));
assert!(trie.contains(&["B", "C"]));
assert!(trie.contains(&["A", "D"]));
assert!(trie.contains(&["D", "E"]));
assert!(trie.contains(&["X", "Y"]));
assert!(trie.contains(&["A"]));
assert!(!trie.contains(&["B", "D"]));
assert!(!trie.contains(&["X", "A"]));
}
#[test]
fn test_empty_sequence() {
let mut trie = SubSequenceTrie::new();
trie.insert_sequence(vec![]);
assert!(trie.contains(&[] as &[&str]));
assert!(!trie.contains(&["A"]));
}
#[test]
fn test_single_element_sequences() {
let sequences = vec![
vec!["A".to_string()],
vec!["B".to_string()],
vec!["C".to_string()],
];
let trie = SubSequenceTrie::from_sequences(sequences);
assert!(trie.contains(&["A"]));
assert!(trie.contains(&["B"]));
assert!(trie.contains(&["C"]));
assert!(!trie.contains(&["D"]));
assert!(!trie.contains(&["A", "B"]));
}
#[test]
fn test_overlapping_sequences() {
let sequences = vec![
vec!["A".to_string(), "B".to_string(), "C".to_string()],
vec!["A".to_string(), "B".to_string(), "D".to_string()],
vec!["A".to_string(), "E".to_string()],
];
let trie = SubSequenceTrie::from_sequences(sequences);
assert!(trie.contains(&["A"]));
assert!(trie.contains(&["A", "B"]));
assert!(trie.contains(&["B", "C"]));
assert!(trie.contains(&["B", "D"]));
assert!(trie.contains(&["A", "E"]));
assert!(!trie.contains(&["C", "D"]));
assert!(!trie.contains(&["E", "C"]));
}
#[test]
fn test_duplicate_sequences() {
let sequences = vec![
vec!["A".to_string(), "B".to_string()],
vec!["A".to_string(), "B".to_string()], vec!["C".to_string(), "D".to_string()],
];
let trie = SubSequenceTrie::from_sequences(sequences);
assert!(trie.contains(&["A", "B"]));
assert!(trie.contains(&["C", "D"]));
assert!(trie.contains(&["A"]));
assert!(!trie.contains(&["A", "C"]));
}
#[test]
fn test_long_subsequence_matching() {
let mut trie = SubSequenceTrie::new();
trie.insert_sequence(vec![
"A".to_string(),
"B".to_string(),
"C".to_string(),
"D".to_string(),
"E".to_string(),
"F".to_string(),
]);
assert!(trie.contains(&["A"]));
assert!(trie.contains(&["A", "C"]));
assert!(trie.contains(&["A", "C", "E"]));
assert!(trie.contains(&["B", "D", "F"]));
assert!(!trie.contains(&["C", "A"]));
assert!(!trie.contains(&["F", "E"]));
assert!(!trie.contains(&["A", "G"]));
}
#[test]
fn test_case_sensitivity() {
let mut trie = SubSequenceTrie::new();
trie.insert_sequence(vec!["Hello".to_string(), "World".to_string()]);
assert!(trie.contains(&["Hello"]));
assert!(trie.contains(&["Hello", "World"]));
assert!(!trie.contains(&["hello"]));
assert!(!trie.contains(&["HELLO"]));
}
#[test]
fn test_default_implementation() {
let trie: SubSequenceTrie = Default::default();
assert!(!trie.contains(&["A"]));
}
#[test]
fn test_nested_sequences_still_work_correctly() {
let sequences = vec![
vec!["A".to_string(), "B".to_string()], vec!["A".to_string(), "B".to_string(), "C".to_string()], ];
let trie = SubSequenceTrie::from_sequences(sequences);
assert!(trie.contains(&["A", "B"]));
assert!(trie.contains(&["A", "B", "C"]));
assert!(trie.contains(&["A"]));
assert!(trie.contains(&["B"]));
assert!(trie.contains(&["A", "C"]));
assert!(trie.contains(&["B", "C"]));
}
#[test]
fn test_accepts_both_string_and_str_slices() {
let mut trie = SubSequenceTrie::new();
trie.insert_sequence(vec![
"Hello".to_string(),
"World".to_string(),
"Test".to_string(),
]);
assert!(trie.contains(&["Hello".to_string(), "World".to_string()]));
assert!(trie.contains(&["Hello".to_string(), "Test".to_string()]));
assert!(trie.contains(&["Hello", "World"]));
assert!(trie.contains(&["Hello", "Test"]));
assert!(trie.contains(&["World", "Test"]));
let string_vec = vec!["Hello".to_string()];
let str_slice = ["World"];
assert!(trie.contains(&string_vec));
assert!(trie.contains(&str_slice));
assert!(!trie.contains(&["World", "Hello"])); assert!(!trie.contains(&["Missing"]));
}
}