Quest 7: Namegraph

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

Link to participate: https://everybody.codes/

  • Pyro@programming.dev
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    10 days ago

    Python

    # pairwise helps picking consecutive values easier
    #   [A,B,C,D] -> [AB, BC, CD]
    from itertools import pairwise
    
    # returns names and dict[str, set[str]] of allowed transitions
    def parse_data(data: str):
        transition_map = {}
        lines = data.splitlines()
    
        for rule_str in lines[2:]:
            before, after = rule_str.split(" > ")
            transition_map[before] = set(after.split(','))
    
        names = lines[0].split(',')
        return names, transition_map
    
    # checks if name is valid according to transition_map
    def is_name_valid(transition_map: dict[str, set[str]], name: str) -> bool:
        for a, b in pairwise(name):
            if a not in transition_map or b not in transition_map[a]:
                return False
        return True
    
    
    def part1(data: str):
        names, transition_map = parse_data(data)
        for name in names:
            if is_name_valid(transition_map, name):
                return name
    
    # <assert snipped>
    
    def part2(data: str):
        ans = 0
        names, transition_map = parse_data(data)
        for i, name in enumerate(names):
            if is_name_valid(transition_map, name):
                ans += i + 1
        return ans
    
    # <assert snipped>
    
    def part3(data: str):
        prefixes, transition_map = parse_data(data)
    
        # remove prefixes that are sub-prefixes of others
        #   to avoid double counting
        superset_prefixes = []
        for i, p1 in enumerate(prefixes):
            for j, p2 in enumerate(prefixes):
                if i != j and p1.startswith(p2):
                    break
            else:
                # no break -> p1 is not a sub-prefix
                superset_prefixes.append(p1)
    
        variants = 0
        for prefix in superset_prefixes:
            if not is_name_valid(transition_map, prefix):
                continue
            
            # DFS to count all valid name extensions
            stack = [(prefix[-1], len(prefix))]
            while stack:
                prev, name_len = stack.pop()
                if name_len >= 7:
                    variants += 1
                if name_len == 11:
                    continue
    
                for next_letter in transition_map.get(prev, []):
                    stack.append((next_letter, name_len + 1))
            
        return variants
    
    # <assert snipped>