LC 332 & LC 753


Solution to LeetCode 332 Reconstruct Itinerary and LeetCode 753 Cracking the Safe.

Hierholzer’s algorithm

Write every combination of length n - “Hamilton Path”, and visit every node exactly once. This is different from “Euler Path”, which is to write every combination of length n-1, and visit every node exactly once. [wiki]

LeetCode 332

Reconstruct Itinerary (Hard). [link]

Hierholzer’s algorithm. Convert the graph to a tree, do post-order traversal, greedily append the child to a list such that the it’s the smallest, and reverse the list after all vertices have been visited.

Time complexity O(m log m) where m is the number of edges. Space complexity O(m). heappop to pop edges, which takes O(log m).

class Solution(object):
    def findItinerary(self, tickets):
        """
        :type tickets: List[List[str]]
        :rtype: List[str]
        """
        que = collections.defaultdict(list)
        for frm, to in tickets:
            que[frm].append(to) # construct an adjacency list
        for key in que:
            heapq.heapify(que[key]) # increasing lexical order
            
        def DFS(node):
            while que[node]:
                tmp = heapq.heappop(que[node])
                DFS(tmp)
            stack.append(node) # post-order
            
        stack = list()
        DFS('JFK') # source
        return stack[::-1] # reverse post-order
            

LeetCode 753

Cracking the Safe (Hard). [link]

The number of all possible passwords is k^n, where k is the number of choices of digit and n is the length of the password. The total lengths for all possible passwords is n* k^n. If every password shares n-1 digits suffix of last node, the total length could reduce to k^n + (n-1).

1] Hierholzer’s algorithm. DFS with backtracking. The idea is to DFS all possible password and collect suffix digits.

Time complexity O(n log n). Space complexity O(n).

class Solution(object):
    def crackSafe(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        res = ["0"] * n # starting node / source
        size = k ** n  # number of possible passwords
        visited = set()
        visited.add("".join(res))
        if self.dfs(res, visited, size, n, k):
            return "".join(res)
        return ""
        
    def DFS(self, res, visited, size, n, k):
        if len(visited) == size:
            return True
        node = "".join(res[len(res) - n + 1:]) # slice the final digit
        
        for i in range(k): # consider new digits: 0, 1, 2, ...
            node = node + str(i) # append a new digit, "forward"
            
            if node not in visited: # if node is not visited
                res.append(str(i)) # append the digit
                visited.add(node)
                
                if self.DFS(res, visited, size, n, k):
                    return True
                  
                res.pop() # "backtrack"
                visited.remove(node) # "backtrack"
            node = node[:-1] # "backtrack"

2] Hierholzer’s algorithm. DFS without backtracking.

https://leetcode-cn.com/problems/cracking-the-safe/solution/po-jie-bao-xian-xiang-by-leetcode-solution/

https://leetcode-cn.com/problems/cracking-the-safe/solution/ou-la-tu-zui-qiang-gong-lue-gai-nian-mo-y7ype/


  TOC