LWC 64: 753. Cracking the Safe

传送门:753. Cracking the Safe

Problem:

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:

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

思路:
此题是贪心,比如n = 4, k = 3的情况下,初始构造了”0000”,为了让密码序列最小,我们最大化利用”0000”中的后三位,这样一来就有三种情况:”00000”和”00001”和”00002”,其中第一种是重复的,所以直接舍去。接着继续看最后三位并构造。典型的子问题,用dfs解决。

这里讲些细节问题,先来看初始版本:

初始Java版本:

public String crackSafe(int n, int k) {
        int size = (int) Math.pow(k, n);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; ++i) {
            sb.append("0");
        }
        String init = sb.toString();
        Set<String> vis = new HashSet<>();
        vis.add(init);
        ans = init;
        valid = false;
        int len = n - 1;
        dfs(init, vis, k, size, len);
        return ans;
    }

    String ans;
    boolean valid = false;
    void dfs(String init, Set<String> vis, int k, int size, int len) {
        if (vis.size() == size) {
            ans = init;
            valid = true;
        }
        else {
            if (valid) return;
            int n = init.length();
            for (int j = 0; j < k; ++j) {
                String ns = init.substring(n - len, n) + j;
                if (!vis.contains(ns)) {
                    String nxt = new String(init + j);
                    vis.add(ns);
                    dfs(nxt, vis, k, size, len);
                    vis.remove(ns);
                }
            }
        }
    }

stackoverflow了,堆栈溢出,后来找了下原因,是因为这句话:

String nxt = new String(init + j);

为了让递归返回的时候状态还原,我使用了clone的方法,但这条语句占了大量的内存,即使在指定递归层数内,但也容易stackoverflow。所以做下优化:

String ans;
    boolean dfs(String init, Set<String> vis, int k, int size, int len) {
        if (vis.size() == size) {
            ans = init;
            return true;
        }
        else {
            int n = init.length();
            for (int j = 0; j < k; ++j) {
                String ns = init.substring(n - len, n) + j;
                if (!vis.contains(ns)) {
                    vis.add(ns);
                    init = init + j;
                    if (dfs(init, vis, k, size, len)) return true;
                    vis.remove(ns);
                    init = init.substring(0, init.length() - 1);
                }
            }
        }
        return false;
    }

这样就能AC了,但需要注意状态还原。不过你可以直接在参数中传值,这样不会影响init!

代码如下:

String ans;
    boolean dfs(String init, Set<String> vis, int k, int size, int len) {
        if (vis.size() == size) {
            ans = init;
            return true;
        }
        else {
            int n = init.length();
            for (int j = 0; j < k; ++j) {
                String ns = init.substring(n - len, n) + j;
                if (!vis.contains(ns)) {
                    vis.add(ns);
                    if (dfs(init + j, vis, k, size, len)) return true;
                    vis.remove(ns);
                }
            }
        }
        return false;
    }

Python版本:

def crackSafe(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: str
        """
        goal = k ** n
        init = "0" * n
        self.ans = init
        vis = set()
        vis.add(init)

        def dfs(init, vis, n, k, goal):
            if goal == len(vis):
                self.ans = init
                return True
            else:
                nlen = len(init)
                for i in range(k):
                    ns = init[nlen - n + 1 : nlen] + str(i)
                    if ns not in vis:
                        vis.add(ns)
                        if (dfs(init[:] + str(i), vis, n, k, goal)): return True
                        vis.remove(ns)
            return False

        dfs(init, vis, n, k, goal)
        return self.ans