Sunday, October 16, 2016

Android Unlock Patterns

Given an Android 3x3 key lock screen and two integers m and n, where 1 ≤ m ≤ n ≤ 9, count the total number of unlock patterns of the Android lock screen, which consist of minimum of m keys and maximum n keys.
Rules for a valid pattern:
  1. Each pattern must connect at least m keys and at most n keys.
  2. All the keys must be distinct.
  3. If the line connecting two consecutive keys in the pattern passes through any other keys, the other keys must have previously selected in the pattern. No jumps through non selected key is allowed.
  4. The order of keys used matters.

Explanation:
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |

Invalid move: 4 - 1 - 3 - 6 
Line 1 - 3 passes through key 2 which had not been selected in the pattern.
Invalid move: 4 - 1 - 9 - 2
Line 1 - 9 passes through key 5 which had not been selected in the pattern.
Valid move: 2 - 4 - 1 - 3 - 6
Line 1 - 3 is valid because it passes through key 2, which had been selected in the pattern
Valid move: 6 - 5 - 4 - 1 - 9 - 2
Line 1 - 9 is valid because it passes through key 5, which had been selected in the pattern.
Example:
Given m = 1, n = 1, return 9.

At first look, this is a very complicated problem (indeed it is). The key idea is still enumerating all possibilities. Based on the explanation, we know that invalid moves are those that jumps from one number to another without visiting the intermediate one, e.g., 1 -> 3 without visiting 2, or 3 -> 7 without visiting 5. The idea is to use a matrix to track all those required visits, e.g., jumps[1][3] = 2, jumps[3][7] = 5. Now use backtracking, if current number is i, then check from 1 to 9, say j, if j hasn't been visited and it is a valid move, i.e., jumps[i][j] == 0 or visited[jumps[i][j]], we recursively find moves. The end point is when the length of the code is longer than n.

Note 1, 3, 7, 9 shares the same pattern, so we only needs to traverse one of them then multiply the result by 4, so as 2, 4, 6, 8.


public int numberOfPatterns(int m, int n) {
        if (m > n) {
            return 0;
        }
        int[][] jumps = new int[10][10];
        jumps[1][3] = 2;
        jumps[3][1] = 2;
        jumps[4][6] = 5;
        jumps[6][4] = 5;
        jumps[7][9] = 8;
        jumps[9][7] = 8;
        jumps[1][7] = 4;
        jumps[7][1] = 4;
        jumps[2][8] = 5;
        jumps[8][2] = 5;
        jumps[3][9] = 6;
        jumps[9][3] = 6;
        jumps[1][9] = 5;
        jumps[9][1] = 5;
        jumps[3][7] = 5;
        jumps[7][3] = 5;
        boolean[] visited = new boolean[10];
        int rst = 0;
        rst += (numberOfPatterns(1, 1, m, n, jumps, visited, 0) * 4);
        rst += (numberOfPatterns(2, 1, m, n, jumps, visited, 0) * 4);
        rst += numberOfPatterns(5, 1, m, n, jumps, visited, 0);
        return rst;
    }

    private int numberOfPatterns(int curr, int len, int m, int n, int[][] jumps,
        boolean[] visited, int rst) {
        visited[curr] = true;
        if (len >= m && len <= n) {
            rst++;
            if (len == n) {
                return rst;
            }
        }
        for (int next = 1; next <= 9; next++) {
            if (!visited[next] && (jumps[curr][next] == 0 ||
                visited[jumps[curr][next]])) {
                rst = numberOfPatterns(next, len + 1, m, n, jumps, visited, rst);
            }
        }
        visited[curr] = false;
        return rst;
    }


No comments:

Post a Comment