use std::collections::HashMap; use std::collections::HashSet; use crate::utils; pub fn task1() { let input = utils::read_file("input/day02.txt"); let mut count_two = 0; let mut count_three = 0; for line in input.lines() { let mut counts = [0u8; 26]; for c in line.chars() { counts[(c as usize - 'a' as usize)] += 1; } if counts.iter().any(|count| *count == 2) { count_two += 1; } if counts.iter().any(|count| *count == 3) { count_three += 1; } } println!("Part 1: {}", count_two * count_three); } pub fn task2() { let input = utils::read_file("input/day02.txt"); for x in input.lines() { for y in input.lines() { let mut diff_index = 0; let mut diff_count = 0; for (i, (a, b)) in x.chars().zip(y.chars()).enumerate() { if a != b { diff_index = i; diff_count += 1; } } if diff_count == 1 { println!( "Part 2: {}{}", &x[0..diff_index], &x[diff_index + 1..x.len()] ); return; } } } println!("Part 2: None found!"); } pub fn task2_linear_asterisk() { let input = utils::read_file("input/day02.txt"); let mut hashes = HashSet::::with_capacity(input.len() * input.lines().next().unwrap().len()); for line in input.lines() { for i in 0..line.len() { let string: String = line .chars() .enumerate() .map(|(index, c)| if index == i { '*' } else { c }) .collect(); if hashes.contains(&string) { println!("{} is double", string); return; } else { hashes.insert(string); } } } } pub fn task2_linear() { let input = utils::read_file("input/day02.txt"); // first order of business: create a tree where each input line is sorted // into every nodes same_prefix, if the path leading from the root to that // has that prefix. let mut root = Node::default(); for id in input.lines() { add_id_to_tree(&mut root, &id); } // find a match.. let result = find_some_match(&root); println!("{:?}", result); } fn find_some_match(node: &Node) -> Option { if let Some(result) = check_children_for_match(&node) { return Some(result); } else { for child in node.outgoing.values() { if let Some(result) = find_some_match(&child) { return Some(result); } } None } } /// Checks all IDs that have the prefix of this node if they are a match. /// For this first for every child we look at its collected IDs - those /// are the potential candidates, e.g. an ID that is in the 'e' child and /// one that is in the 'f' child, if both have the same suffix. /// /// We know that there is a unique match. Therefore we can sort out all /// suffixes that appear more than once in a child node. Then we look at /// all possible suffixes from all child nodes. If one suffix appears exactly /// twice, we have a match. fn check_children_for_match<'a>(node: &Node<'a>) -> Option { let edges: Vec<_> = node.outgoing.keys().collect(); // create a set of candidate suffixes for each edge let suffix_candidates: HashMap> = edges .iter() .map(|c| { let mut suffix_count = HashMap::<&str, HashSet<&str>>::new(); let ref ids = node.outgoing.get(&c).unwrap().same_prefix; for id in ids { suffix_count .entry(&id[node.depth + 1..]) .or_insert(HashSet::new()) .insert(id); } ( **c, suffix_count .iter() .filter(|(_, count)| count.len() == 1) .map(|(suffix, _)| *suffix) .collect(), ) }).collect(); // go over all suffixes and count their occurences. If # = 2, match! let mut suffix_counter: HashMap<&str, usize> = HashMap::new(); for suffix_set in suffix_candidates.values() { for suffix in suffix_set { *suffix_counter.entry(suffix).or_insert(0) += 1; } } if let Some(suffix) = suffix_counter .iter() .find_map(|(suffix, count)| if *count == 2 { Some(suffix) } else { None }) { Some(format!("{}{}", node.prefix, &suffix)) } else { None } } #[derive(Default, Debug)] struct Node<'a> { depth: usize, prefix: &'a str, same_prefix: HashSet<&'a str>, outgoing: HashMap>, } fn add_id_to_tree<'a>(root: &mut Node<'a>, id: &'a str) { let mut current = root; current.same_prefix.insert(&id); for (i, c) in id.chars().enumerate() { { let mut next = current.outgoing.entry(c).or_insert(Node::default()); next.depth = i + 1; next.same_prefix.insert(&id); next.prefix = &id[..=i]; } current = { current }.outgoing.get_mut(&c).unwrap(); } }