Skip to content

Commit

Permalink
Add problem 2115: Find All Possible Recipes from Given Supplies
Browse files Browse the repository at this point in the history
  • Loading branch information
EFanZh committed Aug 7, 2024
1 parent caa050b commit 97ab175
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,7 @@ pub mod problem_2108_find_first_palindromic_string_in_the_array;
pub mod problem_2109_adding_spaces_to_a_string;
pub mod problem_2110_number_of_smooth_descent_periods_of_a_stock;
pub mod problem_2114_maximum_number_of_words_found_in_sentences;
pub mod problem_2115_find_all_possible_recipes_from_given_supplies;
pub mod problem_2116_check_if_a_parentheses_string_can_be_valid;
pub mod problem_2119_a_number_after_a_double_reversal;
pub mod problem_2121_intervals_between_identical_elements;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
pub struct Solution;

// ------------------------------------------------------ snip ------------------------------------------------------ //

use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::{iter, mem};

impl Solution {
fn intern<'a>(word_to_id: &mut HashMap<&'a str, u16>, word: &'a str) -> u16 {
let candidate = word_to_id.len() as _;

match word_to_id.entry(word) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
entry.insert(candidate);

candidate
}
}
}

pub fn find_all_recipes(recipes: Vec<String>, ingredients: Vec<Vec<String>>, supplies: Vec<String>) -> Vec<String> {
let mut word_to_id = (0..).zip(&recipes).map(|(id, recipe)| (recipe.as_str(), id)).collect();

let mut in_degrees = ingredients
.iter()
.map(|ingredients| ingredients.len() as u8)
.collect::<Box<_>>();

let mut graph = Vec::<Vec<u16>>::new();

for (recipe, ingredients) in (0..).zip(&ingredients) {
for ingredient in ingredients {
let ingredient = usize::from(Self::intern(&mut word_to_id, ingredient));

if let Some(nexts) = graph.get_mut(ingredient) {
nexts.push(recipe);
} else {
let extra = ingredient - graph.len();

graph.extend(iter::repeat_with(Vec::new).take(extra));
graph.push(vec![recipe]);
}
}
}

let mut result = Vec::new();

let mut queue = supplies
.iter()
.filter_map(|supply| word_to_id.get(supply.as_str()).copied())
.collect::<VecDeque<_>>();

drop(word_to_id);

let mut recipes = recipes;

while !queue.is_empty() {
for _ in 0..queue.len() {
let ingredient = usize::from(queue.pop_front().unwrap());

for &recipe in &graph[ingredient] {
let recipe = usize::from(recipe);
let in_degree = &mut in_degrees[recipe];

if *in_degree == 1 {
result.push(mem::take(&mut recipes[recipe]));
queue.push_back(recipe as _);
} else {
*in_degree -= 1;
}
}
}
}

result
}
}

// ------------------------------------------------------ snip ------------------------------------------------------ //

impl super::Solution for Solution {
fn find_all_recipes(recipes: Vec<String>, ingredients: Vec<Vec<String>>, supplies: Vec<String>) -> Vec<String> {
Self::find_all_recipes(recipes, ingredients, supplies)
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_solution() {
super::super::tests::run::<super::Solution>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
pub mod bfs;

pub trait Solution {
fn find_all_recipes(recipes: Vec<String>, ingredients: Vec<Vec<String>>, supplies: Vec<String>) -> Vec<String>;
}

#[cfg(test)]
mod tests {
use super::Solution;
use crate::test_utilities;

pub fn run<S: Solution>() {
let test_cases = [
(
(
&["bread"] as &[_],
&[&["yeast", "flour"] as &[_]] as &[&[_]],
&["yeast", "flour", "corn"] as &[_],
),
&["bread"] as &[_],
),
(
(
&["bread", "sandwich"],
&[&["yeast", "flour"], &["bread", "meat"]],
&["yeast", "flour", "meat"],
),
&["bread", "sandwich"],
),
(
(
&["bread", "sandwich", "burger"],
&[&["yeast", "flour"], &["bread", "meat"], &["sandwich", "meat", "bread"]],
&["yeast", "flour", "meat"],
),
&["bread", "burger", "sandwich"],
),
(
(
&["ju", "fzjnm", "x", "e", "zpmcz", "h", "q"],
&[
&["d"],
&["hveml", "f", "cpivl"],
&["cpivl", "zpmcz", "h", "e", "fzjnm", "ju"],
&["cpivl", "hveml", "zpmcz", "ju", "h"],
&["h", "fzjnm", "e", "q", "x"],
&["d", "hveml", "cpivl", "q", "zpmcz", "ju", "e", "x"],
&["f", "hveml", "cpivl"],
],
&["f", "hveml", "cpivl", "d"],
),
&["fzjnm", "ju", "q"],
),
];

for ((recipes, ingredients, supplies), expected) in test_cases {
assert_eq!(
test_utilities::unstable_sorted(S::find_all_recipes(
recipes.iter().copied().map(str::to_string).collect(),
ingredients
.iter()
.map(|ingredients| ingredients.iter().copied().map(str::to_string).collect())
.collect(),
supplies.iter().copied().map(str::to_string).collect(),
)),
expected,
);
}
}
}

0 comments on commit 97ab175

Please sign in to comment.