use std::collections::HashMap;
#[derive(Default)]
struct TrieNode {
children: HashMap<char, TrieNode>,
pattern_indices: Vec<usize>,
}
pub fn build_propagation_table(prefixes: &[String]) -> Vec<Vec<usize>> {
let mut root = TrieNode::default();
for (idx, prefix) in prefixes.iter().enumerate() {
let mut node = &mut root;
for ch in prefix.chars() {
node = node.children.entry(ch).or_default();
}
node.pattern_indices.push(idx);
}
let mut propagation: Vec<Vec<usize>> = vec![Vec::new(); prefixes.len()];
collect_propagation(&root, &mut propagation);
propagation
}
fn collect_propagation(node: &TrieNode, propagation: &mut [Vec<usize>]) -> Vec<usize> {
let mut subtree_indices = node.pattern_indices.clone();
let mut descendant_indices = Vec::new();
for child in node.children.values() {
let child_subtree = collect_propagation(child, propagation);
descendant_indices.extend_from_slice(&child_subtree);
subtree_indices.extend_from_slice(&child_subtree);
}
for &idx in &node.pattern_indices {
propagation[idx] = descendant_indices.clone();
}
subtree_indices
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_propagation() {
let prefixes = vec![
"sk-".to_string(), "sk-proj-".to_string(), "sk-ant-".to_string(), "ghp_".to_string(), ];
let table = build_propagation_table(&prefixes);
assert!(table[0].contains(&1));
assert!(table[0].contains(&2));
assert!(!table[0].contains(&3));
assert!(table[1].is_empty());
assert!(table[3].is_empty());
}
#[test]
fn no_self_propagation() {
let prefixes = vec!["abc".to_string(), "abcd".to_string()];
let table = build_propagation_table(&prefixes);
assert_eq!(table[0], vec![1]);
assert!(!table[0].contains(&0));
}
#[test]
fn deep_chain() {
let prefixes = vec![
"a".to_string(), "ab".to_string(), "abc".to_string(), "abcd".to_string(), ];
let table = build_propagation_table(&prefixes);
assert_eq!(table[0].len(), 3);
assert_eq!(table[2], vec![3]);
assert!(table[3].is_empty());
}
#[test]
fn empty_input() {
let table = build_propagation_table(&[]);
assert!(table.is_empty());
}
}