diff --git a/aoc2020/src/day21.rs b/aoc2020/src/day21.rs index f0be902..91025e2 100644 --- a/aoc2020/src/day21.rs +++ b/aoc2020/src/day21.rs @@ -10,6 +10,7 @@ pub fn run() -> Result { let mut res = String::with_capacity(128); writeln!(res, "part 1: {}", part1(INPUT)?)?; + writeln!(res, "part 2: {}", part2(INPUT)?)?; Ok(res) } @@ -39,6 +40,56 @@ fn part1(input: &str) -> Result { Ok(not_allergens.map(|word| all_words[word]).sum()) } +fn part2(input: &str) -> Result { + let mut matchings: AllergenMatchings = input.try_into()?; + let mut allergens_to_identify: Vec<&str> = matchings.0.keys().copied().collect(); + + for _ in 0..matchings.0.len() { + let allergen = allergens_to_identify + .iter() + .min_by_key(|&name| matchings.0[name].len()) + .copied() + .expect("should always have at least one allergen to identify"); + + // the algorithm only works if we can always find an allergen with only one possible + // assignation + assert_eq!(matchings.0[allergen].len(), 1); + + let allergen_translation = matchings.0[allergen].iter().copied().next().unwrap(); + + matchings + .0 + .iter_mut() + .filter(|(&allerg, _)| allerg != allergen) + .for_each(|(_, possible_matchings)| { + possible_matchings.remove(allergen_translation); + }); + + allergens_to_identify.swap_remove( + allergens_to_identify + .iter() + .position(|al| *al == allergen) + .unwrap(), + ); + } + + // Vec of (allergen, translation) + let mut matchings: Vec<(&str, &str)> = matchings + .0 + .iter() + .map(|(&key, possibilities)| (key, possibilities.iter().copied().next().unwrap())) + .collect(); + + matchings.sort_unstable(); + + let canonical_ingredient_list: Vec<&str> = matchings + .iter() + .map(|(_, translation)| *translation) + .collect(); + + Ok(canonical_ingredient_list.join(",")) +} + #[derive(Debug)] struct AllergenMatchings<'a>(HashMap<&'a str, HashSet<&'a str>>); @@ -84,4 +135,17 @@ mod tests { fn part1_real() { assert_eq!(part1(INPUT).unwrap(), 2315); } + + #[test] + fn part2_provided() { + assert_eq!(part2(PROVIDED).unwrap(), "mxmxvkd,sqjhc,fvjkl"); + } + + #[test] + fn part2_real() { + assert_eq!( + part2(INPUT).unwrap(), + "cfzdnz,htxsjf,ttbrlvd,bbbl,lmds,cbmjz,cmbcm,dvnbh" + ); + } }