2020: d21: ex2: add solution
This commit is contained in:
parent
d412cac964
commit
9f0f9be2d1
81
2020/d21/ex2/ex2.py
Executable file
81
2020/d21/ex2/ex2.py
Executable file
|
@ -0,0 +1,81 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
|
||||
def parse(raw: List[str]) -> Tuple[List[Set[str]], List[Set[str]]]:
|
||||
def parse_ingredients(line: str) -> Set[str]:
|
||||
pos = line.find(" (contains ")
|
||||
if pos != -1:
|
||||
line = line[:pos]
|
||||
return set(line.split())
|
||||
|
||||
def parse_allergens(line: str) -> Set[str]:
|
||||
pos = line.find("(contains ")
|
||||
if pos == -1:
|
||||
return set()
|
||||
return set(re.findall("([^ ]+)[,\\)]", line))
|
||||
|
||||
ingredients = []
|
||||
allergens = []
|
||||
|
||||
for line in raw:
|
||||
ingredients.append(parse_ingredients(line))
|
||||
allergens.append(parse_allergens(line))
|
||||
|
||||
return ingredients, allergens
|
||||
|
||||
|
||||
def find_allergens(
|
||||
ingredients: List[Set[str]], allergens: List[Set[str]]
|
||||
) -> Dict[str, Set[str]]:
|
||||
all_ingredients = functools.reduce(lambda lhs, rhs: lhs | rhs, ingredients)
|
||||
possibilities: Dict[str, Set[str]] = defaultdict(lambda: deepcopy(all_ingredients))
|
||||
|
||||
for ing, all in zip(ingredients, allergens):
|
||||
for allergen in all:
|
||||
possibilities[allergen] &= set(ing)
|
||||
|
||||
return dict(possibilities)
|
||||
|
||||
|
||||
def cross_eliminate(possibilities: Dict[str, Set[str]]) -> None:
|
||||
while True:
|
||||
eliminated = False
|
||||
for pos in possibilities:
|
||||
if len(possibilities[pos]) != 1:
|
||||
continue
|
||||
for other_pos in possibilities:
|
||||
if other_pos == pos:
|
||||
continue
|
||||
if len(possibilities[other_pos] & possibilities[pos]) == 0:
|
||||
continue
|
||||
eliminated = True
|
||||
possibilities[other_pos] -= possibilities[pos]
|
||||
if not eliminated:
|
||||
break
|
||||
|
||||
|
||||
def solve(raw: List[str]) -> str:
|
||||
ingredients, allergens = parse(raw)
|
||||
possibilities = find_allergens(ingredients, allergens)
|
||||
cross_eliminate(possibilities)
|
||||
matches = [
|
||||
(ingredient.pop(), allergen) for allergen, ingredient in possibilities.items()
|
||||
]
|
||||
matches.sort(key=lambda tup: tup[1])
|
||||
return ",".join(ingredient for ingredient, __ in matches)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
input = [line.strip() for line in sys.stdin]
|
||||
print(solve(input))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue