Problem: Find All Possible Recipes from Given Supplies

You have information about `n` different recipes. You are given a string array
`recipes` and a 2D string array `ingredients`. The `ith` recipe has the name
`recipes[i]`, and you can create it if you have all the needed ingredients
from `ingredients[i]`. Ingredients to a recipe may need to be created from
other recipes, i.e., `ingredients[i]` may contain a string that is in
`recipes`.

You are also given a string array `supplies` containing all the ingredients
that you initially have, and you have an infinite supply of all of them.

Return a list of all the recipes that you can create. You may return the
answer in any order.

Note that two recipes may contain each other in their ingredients.


Python solution:
import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def findAllRecipes(self, recipes: List[str], ingredients: List[List[str]], supplies: List[str]) -> List[str]:
    ans = []
    supplies = set(supplies)
    graph = collections.defaultdict(list)
    inDegrees = collections.Counter()
    q = collections.deque()

    for i, recipe in enumerate(recipes):
      for ingredient in ingredients[i]:
        if ingredient not in supplies:
          graph[ingredient].append(recipe)
          inDegrees[recipe] += 1

    for recipe in recipes:
      if inDegrees[recipe] == 0:
        q.append(recipe)

    while q:
      u = q.popleft()
      ans.append(u)
      for v in graph[u]:
        inDegrees[v] -= 1
        if inDegrees[v] == 0:
          q.append(v)

    return ans


Java solution:
class Solution {
  public List<String> findAllRecipes(String[] recipes, List<List<String>> ingredients,
                                     String[] supplies) {
    List<String> ans = new ArrayList<>();
    Set<String> suppliesSet = new HashSet<>();
    for (final String supply : supplies)
      suppliesSet.add(supply);
    Map<String, List<String>> graph = new HashMap<>();
    Map<String, Integer> inDegrees = new HashMap<>();

    // Build the graph.
    for (int i = 0; i < recipes.length; ++i)
      for (final String ingredient : ingredients.get(i))
        if (!suppliesSet.contains(ingredient)) {
          graph.putIfAbsent(ingredient, new ArrayList<>());
          graph.get(ingredient).add(recipes[i]);
          inDegrees.merge(recipes[i], 1, Integer::sum);
        }

    // Perform topological sorting.
    Queue<String> q = Arrays.stream(recipes)
                          .filter(recipe -> inDegrees.getOrDefault(recipe, 0) == 0)
                          .collect(Collectors.toCollection(ArrayDeque::new));

    while (!q.isEmpty()) {
      final String u = q.poll();
      ans.add(u);
      if (!graph.containsKey(u))
        continue;
      for (final String v : graph.get(u)) {
        inDegrees.merge(v, -1, Integer::sum);
        if (inDegrees.get(v) == 0)
          q.offer(v);
      }
    }

    return ans;
  }
}


C++ solution:
class Solution {
 public:
  vector<string> findAllRecipes(vector<string>& recipes,
                                vector<vector<string>>& ingredients,
                                vector<string>& supplies) {
    vector<string> ans;
    unordered_set<string> suppliesSet(supplies.begin(), supplies.end());
    unordered_map<string, vector<string>> graph;
    unordered_map<string, int> inDegrees;
    queue<string> q;

    // Build the graph.
    for (int i = 0; i < recipes.size(); ++i)
      for (const string& ingredient : ingredients[i])
        if (!suppliesSet.contains(ingredient)) {
          graph[ingredient].push_back(recipes[i]);
          ++inDegrees[recipes[i]];
        }

    // Perform topological sorting.
    for (const string& recipe : recipes)
      if (!inDegrees.contains(recipe))
        q.push(recipe);

    while (!q.empty()) {
      const string u = q.front();
      q.pop();
      ans.push_back(u);
      if (!graph.contains(u))
        continue;
      for (const string& v : graph[u])
        if (--inDegrees[v] == 0)
          q.push(v);
    }

    return ans;
  }
};