Solving Leetcode Interviews in Seconds with AI: Beautiful Arrangement
Introduction
In this blog post, we will explore how to solve the LeetCode problem "526" using AI. LeetCode is a popular platform for preparing for coding interviews, and with the help of AI tools like Chatmagic, we can generate solutions quickly and efficiently - helping you pass the interviews and get the job offer without having to study for months.
Problem Statement
Suppose you have n integers labeled 1 through n. A permutation of those n integers perm (1-indexed) is considered a beautiful arrangement if for every i (1 <= i <= n), either of the following is true: perm[i] is divisible by i. i is divisible by perm[i]. Given an integer n, return the number of the beautiful arrangements that you can construct. Example 1: Input: n = 2 Output: 2 Explanation: The first beautiful arrangement is [1,2]: - perm[1] = 1 is divisible by i = 1 - perm[2] = 2 is divisible by i = 2 The second beautiful arrangement is [2,1]: - perm[1] = 2 is divisible by i = 1 - i = 2 is divisible by perm[2] = 1 Example 2: Input: n = 1 Output: 1 Constraints: 1 <= n <= 15
Explanation
Here's the solution:
- Backtracking with Pruning: The core idea is to use backtracking to explore all possible permutations. However, we add a crucial pruning step: at each position
i, we only consider numbers that satisfy the beautiful arrangement condition (eitherperm[i]is divisible byioriis divisible byperm[i]). This significantly reduces the search space. - Bitmasking for Visited Numbers: We use a bitmask to keep track of which numbers have already been used in the permutation. This allows for efficient checking of availability and marking/unmarking during the backtracking process.
Dynamic Programming (Memoization): Even with backtracking and pruning, exploring all permutations for n=15 can still be time-consuming. To further optimize, we can use memoization, storing the results of subproblems (the number of arrangements possible given a specific state - current index and used numbers). The
lru_cachedecorator makes this easy.Runtime Complexity: O(n!), where n is the input integer. Because of pruning and memoization, it is significantly faster than raw O(n!). Storage Complexity: O(n * 2^n) due to memoization and bitmasking.
Code
from functools import lru_cache
def countArrangement(n: int) -> int:
"""
Calculates the number of beautiful arrangements for n integers.
Args:
n: The number of integers (1 to n).
Returns:
The number of beautiful arrangements.
"""
@lru_cache(None)
def solve(index: int, mask: int) -> int:
"""
Recursive helper function to count arrangements.
Args:
index: The current index to fill (1-indexed).
mask: A bitmask representing which numbers have been used.
Returns:
The number of beautiful arrangements possible from this state.
"""
if index > n:
return 1 # Base case: all positions filled
count = 0
for num in range(1, n + 1):
if not (mask & (1 << (num - 1))): # Check if num is not used
if (num % index == 0) or (index % num == 0):
count += solve(index + 1, mask | (1 << (num - 1)))
return count
return solve(1, 0)