753. Cracking the Safe

There is a box protected by a password. The password is n digits, where each letter can be one of the first k digits 0, 1, ..., k-1.

You can keep inputting the password, the password will automatically be matched against the last n digits entered.

For example, assuming the password is "345", I can open it when I type "012345", but I enter a total of 6 digits.

Please return any string of minimum length that is guaranteed to open the box after the entire string is inputted.

Example 1:

Input: n = 1, k = 2
Output: "01"
Note: "10" will be accepted too.

Example 2:

Input: n = 2, k = 2
Output: "00110"
Note: "01100", "10011", "11001" will be accepted too.

Note:

  1. n will be in the range [1, 4].
  2. k will be in the range [1, 10].
  3. k^n will be at most 4096.

思路:https://www.youtube.com/watch?v=iPLQgXUiU14

欧拉回路,dfs,关键是重复利用前n-1位

Approach #1: Euler Circuit [Accepted]

Intuition

We can think of this problem as the problem of finding an Euler path (a path visiting every edge exactly once) on the following graph: there are k^{n-1}kn1 nodes with each node having kk edges.

For example, when k = 4, n = 3, the nodes are '00', '01', '02', ..., '32', '33' and each node has 4 edges '0', '1', '2', '3'. A node plus edge represents a complete edge and viewing that substring in our answer.

Because our graph is highly connected and symmetric, we should expect intuitively that taking any path greedily in some order will probably result in an Euler path. Actually, this intuition is also a theorem: any connected directed graph where all nodes have equal in-degree and out-degree has an Euler circuit (an Euler path ending where it started.)

Algorithm

We will modify our standard depth-first search: instead of keeping track of nodes, we keep track of (complete) edges: seen records if an edge has been visited.

Also, we'll need to visit in a sort of "post-order", recording the answer after visiting the edge. This is to prevent deadends. For example, with k = 2, n = 2, we have the nodes '0', '1'. If we greedily visit complete edges '00', '01', '10', we will be stuck. However, if we visit in post-order, we'll end up visiting '00', '01', '11', '10' correctly.

In general, because of the situation where taking some edge will make us stuck, we should try to travel and visit other edges first.

Complexity Analysis

  • Time Complexity: O(n * k^n)O(nkn). We visit every edge once in our depth-first search, and nodes take O(n)O(n)space.

  • Space Complexity: O(n * k^n)O(nkn), the size of seen.


class Solution(object):
    def crackSafe(n, k):
        seen = set()
        ans = []
        def dfs(node):
            for x in map(str, range(k)):
                nei = node + x
                if nei not in seen:
                    seen.add(nei)
                    dfs(nei[1:])
                    ans.append(x)

        dfs("0" * (n-1))
        return "".join(ans) + "0" * (n-1)



Approach #2: Inverse Burrows-Wheeler Transform [Accepted]

Explanation

If we are familiar with the theory of combinatorics on words, recall that a Lyndon Word L is a word that is the unique minimum of it's rotations.

One important mathematical result (due to Fredericksen and Maiorana), is that the concatenation in lexicographic order of Lyndon words with length dividing n, forms a de Bruijin sequence: a sequence where every every word (from the k^nkn available) appears as a substring of length n (where we are allowed to wrap around.)

For example, when n = 6, k = 2, all the Lyndon words with length dividing n in lexicographic order are (spaces for convenience): 0 000001 000011 000101 000111 001 001011 001101 001111 01 010111 011 011111 1. It turns out this is the smallest de Bruijin sequence.

We can use the Inverse Burrows-Wheeler Transform (IBWT) to generate these Lyndon words. Consider two sequences: S is the alphabet repeated k^{n-1}kn1 times: S = 0123...0123...0123...., and S' is the alphabet repeated k^{n-1}kn1 times for each letter: S' = 00...0011...1122.... We can think of S' and S as defining a permutation, where the j-th occurrence of each letter of the alphabet in S' maps to the corresponding j-th occurrence in S. The cycles of this permutation turn out to be the corresponding smallest de Bruijin sequence (link).

Under this view, the permutation S' \rightarrow SSS [mapping permutation indices (i * k^{n-1} + q) \rightarrow (q * k + i)(ikn1+q)(qk+i)] form the desired Lyndon words.

Complexity Analysis

  • Time Complexity: O(k^n)O(kn). We loop through every possible substring.

  • Space Complexity: O(k^n)O(kn), the size of P and ans.

class Solution(object):
    def crackSafe(self, n, k):
        M = k**(n-1)
        P = [q*k+i for i in xrange(k) for q in xrange(M)]
        ans = []

        for i in xrange(k**n):
            j = i
            while P[j] >= 0:
                ans.append(str(j / M))
                P[j], j = -1, P[j]

        return "".join(ans) + "0" * (n-1)




class Solution(object):
    def crackSafe(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        ans = "0" * (n - 1)
        visits = set()
        for x in range(k ** n):
            current = ans[-n+1:] if n > 1 else ''
            for y in range(k - 1, -1, -1):
                if current + str(y) not in visits:
                    visits.add(current + str(y))
                    ans += str(y)
                    break
        return ans

def crackSafe(self, n, k):
    s = '0' * (n - 1)
    D = '9876543210'[-k:]
    for _ in range(k**n):
        s += next(d for d in D if (s + d)[-n:] not in s)
    return s


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值