|
| 1 | +# Find All Possible Recipes from Given Supplies |
| 2 | + |
| 3 | +You are given information about n different recipes. Each recipe is listed in the array recipes, and its corresponding |
| 4 | +ingredients are provided in the 2D array ingredients. The ith recipe, recipes[i], can be prepared if all the necessary |
| 5 | +ingredients listed in ingredients[i] are available. Some ingredients might need to be created from other recipes, meaning |
| 6 | +ingredients[i] may contain strings that are also in recipes. |
| 7 | + |
| 8 | +Additionally, you have a string array supplies that contains all the ingredients you initially have, and you have an |
| 9 | +infinite supply of each. |
| 10 | + |
| 11 | +Return a list of all the recipes you can create. The answer can be returned in any order. |
| 12 | + |
| 13 | +> Note: It is possible for two recipes to list each other as ingredients. However, if these are the only two recipes |
| 14 | +> provided, the expected output is an empty list. |
| 15 | +
|
| 16 | +## Constraints |
| 17 | + |
| 18 | +- `n` == `recipes.length` == `ingredients.length` |
| 19 | +- 1 <= `n` <= 100 |
| 20 | +- 1 ≤ `ingredients[i].length`, `supplies.length` ≤ 100 |
| 21 | +- 1 ≤ `recipes[i].length`, `ingredients[i][j].length`, `supplies[k].length` ≤ 10 |
| 22 | +- `recipes[i]`, `ingredients[i][j]`, and `supplies[k]` consist only of lowercase English letters. |
| 23 | +- All the combined values of `recipes` and `supplies` are unique. |
| 24 | +- Each `ingredients[i]` doesn’t contain any duplicate values. |
| 25 | + |
| 26 | +## Examples |
| 27 | + |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | +## Solution |
| 32 | + |
| 33 | +An optimized approach to solve this problem would be to use the topological sort pattern. We need to understand that the |
| 34 | +preparation of recipes depends on the availability of ingredients. Some ingredients are available initially, while others |
| 35 | +may need to be prepared using other recipes. This dependency between recipes and their ingredients can be represented as |
| 36 | +a directed graph where: |
| 37 | + |
| 38 | +- Each recipe is a node. |
| 39 | +- There is a directed edge from node A to node B if the preparation of recipe B requires recipe A. |
| 40 | + |
| 41 | +Topological sort is an algorithm commonly used to order nodes (tasks) in a directed acyclic graph (DAG) such that for |
| 42 | +every directed edge u → v, node u comes before node v in the ordering. In our case, this means that if a recipe A depends |
| 43 | +on recipe B, then B should come before A in the order of recipes we can prepare. |
| 44 | + |
| 45 | +If there are circular dependencies (like two recipes requiring each other), these will be cycles in the graph, and we |
| 46 | +cannot prepare either recipe. The goal of using topological sort is to determine the order in which recipes can be prepared, |
| 47 | +starting from those that can be made with the initial supplies and progressing through those that depend on previously |
| 48 | +prepared recipes. |
| 49 | + |
| 50 | +Let’s go through the algorithm to see how we will reach the solution: |
| 51 | + |
| 52 | +1. Initialize data structures: |
| 53 | + - `recipe_graph`: This is a dictionary where each key is a recipe and its value is a list of recipes that depend on it. |
| 54 | + - `recipe_indegrees`: This is a dictionary that stores the number of prerequisites for each recipe. |
| 55 | + - `recipes_queue`: This is a queue that processes recipes, which can be immediately made with the initial supplies. |
| 56 | + - `possible_recipes`: This is an array that stores all the recipes that can be created. |
| 57 | +2. Build the graph and in-degree count by iterating over each ingredient of each recipe: |
| 58 | + - If the ingredient is another recipe, add an edge from that ingredient to the current recipe in `recipe_graph` and |
| 59 | + increase `recipe_indegrees` for the current recipe. |
| 60 | + - If the ingredient is available in `supplies`, skip it since it’s immediately available. |
| 61 | +3. Add all recipes with an in-degree of 0 (no prerequisites) to `recipes_queue`. |
| 62 | +4. Process the `recipes_queue` using topological sorting: |
| 63 | + - While `recipes_queue` is not empty: |
| 64 | + - Pop a recipe from the queue. |
| 65 | + - Add it to `possible_recipes` since it can now be made. |
| 66 | + - For each recipe that depends on this recipe, reduce its in-degree by 1. |
| 67 | + - If any recipe’s in-degree becomes 0, add it to `recipes_queue`. |
| 68 | +5. Return the `possible_recipes` list, which contains all the recipes that can be created with the given supplies. |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | +### Time Complexity |
| 82 | + |
| 83 | +The time complexity of this solution is O(v+e), where v is the vertices of the recipe_graph and e is the edges of the |
| 84 | +graph. This is because we traverse each vertex and edge in the graph exactly once during the topological sort, and the |
| 85 | +dictionary data structure allows us to access vertices and edges in constant time. |
| 86 | + |
| 87 | +### Space Complexity |
| 88 | + |
| 89 | +The space complexity is also O(v+e) as we have to store all the recipes and their respective ingredients. |
0 commit comments