Android Unlock Patterns

XXX. Android Lock Pattern

DFS
Backtracking

Problem Statement:

Calculate the number of possible Android unlock patterns of length between m and n, where dots must be connected following specific rules.

  • Pattern must be of length m to n
  • Must pass through middle point to connect opposite corners
  • Cannot skip over unvisited points

Implementation:


public class Solution {
    public int numberOfPatterns(int m, int n) {
        // Skip array represents the number that must be visited
        // to go from index i to index j
        int[][] skip = new int[10][10];
        
        // Initialize skip array with required points
        skip[1][3] = skip[3][1] = 2;            // Horizontal skips
        skip[1][7] = skip[7][1] = 4;            // Vertical skips
        skip[3][9] = skip[9][3] = 6;
        skip[7][9] = skip[9][7] = 8;
        // Diagonal skips
        skip[1][9] = skip[9][1] = skip[2][8] = skip[8][2] = 
        skip[3][7] = skip[7][3] = skip[4][6] = skip[6][4] = 5;
        
        boolean[] visited = new boolean[10];      // Track visited numbers
        int count = 0;
        
        // Try all pattern lengths from m to n
        for (int i = m; i <= n; i++) {
            // Multiply by 4 due to symmetry:
            // 2,4,6,8 are symmetric
            count += dfs(2, visited, i - 1, skip) * 4;  
            // 1,3,7,9 are symmetric
            count += dfs(1, visited, i - 1, skip) * 4;  
            // 5 is unique
            count += dfs(5, visited, i - 1, skip);      
        }
        
        return count;
    }
    
    private int dfs(int num, boolean[] visited, int remain, int[][] skip) {
        // Base case: no more numbers needed
        if (remain == 0) return 1;
        
        visited[num] = true;           // Mark current number as visited
        int count = 0;
        
        // Try all possible next numbers
        for (int i = 1; i <= 9; i++) {
            // Can visit if:
            // 1. Number not visited
            // 2. No skip number exists OR skip number is visited
            if (!visited[i] && (skip[num][i] == 0 || visited[skip[num][i]])) {
                count += dfs(i, visited, remain - 1, skip);
            }
        }
        
        visited[num] = false;          // Restore state for backtracking
        return count;
    }
}

Python Solution:


class Solution:
    def numberOfPatterns(self, m: int, n: int) -> int:
        # Initialize skip matrix
        skip = [[0] * 10 for _ in range(10)]
        
        # Set up horizontal and vertical skips
        skip[1][3] = skip[3][1] = 2
        skip[1][7] = skip[7][1] = 4
        skip[3][9] = skip[9][3] = 6
        skip[7][9] = skip[9][7] = 8
        
        # Set up diagonal skips all through center (5)
        diagonals = [(1,9), (2,8), (3,7), (4,6)]
        for i, j in diagonals:
            skip[i][j] = skip[j][i] = 5
        
        def dfs(num: int, visited: set, remain: int) -> int:
            if remain == 0:
                return 1
                
            count = 0
            visited.add(num)
            
            for next_num in range(1, 10):
                if (next_num not in visited and 
                    (skip[num][next_num] == 0 or 
                     skip[num][next_num] in visited)):
                    count += dfs(next_num, visited, remain - 1)
                    
            visited.remove(num)
            return count
        
        count = 0
        visited = set()
        
        # Calculate patterns for each length
        for i in range(m, n + 1):
            count += dfs(2, visited, i - 1) * 4  # 2,4,6,8
            count += dfs(1, visited, i - 1) * 4  # 1,3,7,9
            count += dfs(5, visited, i - 1)      # center
            
        return count

C++ Solution:


class Solution {
private:
    int dfs(int num, vector& visited, int remain, 
            vector>& skip) {
        if (remain == 0) return 1;
        
        visited[num] = true;
        int count = 0;
        
        for (int i = 1; i <= 9; i++) {
            if (!visited[i] && 
                (skip[num][i] == 0 || visited[skip[num][i]])) {
                count += dfs(i, visited, remain - 1, skip);
            }
        }
        
        visited[num] = false;
        return count;
    }
    
public:
    int numberOfPatterns(int m, int n) {
        vector> skip(10, vector(10, 0));
        
        // Initialize skip array
        skip[1][3] = skip[3][1] = 2;
        skip[1][7] = skip[7][1] = 4;
        skip[3][9] = skip[9][3] = 6;
        skip[7][9] = skip[9][7] = 8;
        
        // Diagonal skips through center
        const vector> diagonals = {
            {1,9}, {2,8}, {3,7}, {4,6}
        };
        for (const auto& [i, j] : diagonals) {
            skip[i][j] = skip[j][i] = 5;
        }
        
        vector visited(10, false);
        int count = 0;
        
        for (int i = m; i <= n; i++) {
            count += dfs(2, visited, i - 1, skip) * 4;  // Corners
            count += dfs(1, visited, i - 1, skip) * 4;  // Edges
            count += dfs(5, visited, i - 1, skip);      // Center
        }
        
        return count;
    }
};

Complexity:

Time Complexity: O(n!), as we try all possible combinations
Space Complexity: O(n) for recursion stack

Key Points:

  • **Skip Array**:
    • Tracks required points between two positions
    • Handles both straight and diagonal lines
    • Zero means direct connection possible
  • **Pattern Symmetry**:
    • Corner numbers (1,3,7,9) are symmetric
    • Edge numbers (2,4,6,8) are symmetric
    • Center (5) is unique
Previous
Previous

Task Scheduler

Next
Next

Find score of array after marking all elements