LeetCode 3577 - Count the Number of Computer Unlocking Permutations
The problem presents a set of n computers, each with a unique password complexity represented in an array complexity of length n. The computers are labeled 0 to n - 1, and only computer 0 is initially unlocked.
Difficulty: 🟡 Medium
Topics: Array, Math, Brainteaser, Combinatorics
Solution
Problem Understanding
The problem presents a set of n computers, each with a unique password complexity represented in an array complexity of length n. The computers are labeled 0 to n - 1, and only computer 0 is initially unlocked. To unlock another computer i, you must already have unlocked at least one computer j such that j < i and complexity[j] < complexity[i]. In other words, you can only unlock computers in a way that respects both index order and increasing complexity relative to some previously unlocked computer.
The input is the complexity array, and the expected output is the number of valid permutations of computer indices [0, 1, 2, ..., n-1] that allow all computers to be unlocked following the rules, modulo $10^9 + 7$. The constraints are significant: 2 <= n <= 10^5 and 1 <= complexity[i] <= 10^9. This means a brute-force solution that generates all permutations is infeasible.
Edge cases to consider include scenarios where all complexities are identical (making it impossible to unlock beyond the root), strictly increasing or decreasing sequences, and sequences with repeated elements but with some order allowing unlocking.
Approaches
The brute-force approach would generate all permutations of [0, 1, ..., n-1] and validate each permutation by checking the unlocking condition. While correct, it has factorial time complexity $O(n!)$ and is completely impractical for n up to $10^5$.
The key observation for an optimal solution is to recognize the combinatorial structure of the problem. We can treat computers with equal complexity as blocks that must be unlocked after at least one lower complexity computer is unlocked, and we can count the number of ways to interleave the blocks using factorials and modular arithmetic. Specifically, the order of unlocking within the same complexity group is flexible, but a group can only be unlocked if a smaller complexity group has been unlocked before it. If two consecutive complexity groups have the same value, no valid unlocking is possible.
We can use a frequency count of the complexities, sort the unique complexities, and apply combinatorial formulas to count valid permutations using factorials.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n!) | O(n) | Generate all permutations and check validity, infeasible for large n |
| Optimal | O(n log n) | O(n) | Count permutations by complexity groups, using factorials and modular arithmetic |
Algorithm Walkthrough
- Count the frequency of each unique complexity in the array using a hash map. This will allow us to know how many computers belong to each complexity group.
- Sort the unique complexities in increasing order. This ensures we process unlocking in the proper sequence from smallest to largest complexity.
- Initialize
resultas1. This will store the total number of valid permutations modulo $10^9 + 7$. - For each complexity group, calculate the number of ways to unlock its computers given that at least one computer in a lower complexity group is already unlocked. Use the formula $\text{group_size}!$ (factorial of the number of computers in this complexity group) to account for internal permutations.
- If any complexity group has no previously unlocked computer with lower complexity, immediately return
0since it is impossible to unlock this group. - Multiply all factorial contributions modulo $10^9 + 7$ to get the final answer.
Why it works: The algorithm works because it respects the unlocking constraints while using combinatorial counting for groups of equal complexity. Sorting ensures we unlock lower complexity computers first, and factorials account for internal permutations within a complexity group. The invariant is that at each step, we only unlock a group if at least one smaller complexity has been unlocked, satisfying the problem conditions.
Python Solution
from typing import List
from collections import Counter
MOD = 10**9 + 7
class Solution:
def countPermutations(self, complexity: List[int]) -> int:
freq = Counter(complexity)
unique_complexities = sorted(freq.keys())
result = 1
unlocked_so_far = 0
for i, comp in enumerate(unique_complexities):
count = freq[comp]
if i == 0 and comp != complexity[0]:
return 0 # first unlocked must include root computer
if i > 0 and unlocked_so_far == 0:
return 0 # no smaller complexity to unlock this group
# multiply by factorial of group size
for k in range(1, count + 1):
result = (result * k) % MOD
unlocked_so_far += count
return result
Implementation Walkthrough: We first count the frequency of each complexity using Counter. Sorting the unique complexities ensures we process in ascending order. unlocked_so_far keeps track of previously unlocked computers. For each complexity, we multiply result by the factorial of the group size. If the first group does not contain the root or there is no previously unlocked group for higher complexity, we return 0.
Go Solution
package main
func countPermutations(complexity []int) int {
const MOD = 1_000_000_007
n := len(complexity)
freqMap := make(map[int]int)
for _, c := range complexity {
freqMap[c]++
}
unique := make([]int, 0, len(freqMap))
for k := range freqMap {
unique = append(unique, k)
}
// sort unique complexities
for i := 0; i < len(unique); i++ {
for j := i+1; j < len(unique); j++ {
if unique[i] > unique[j] {
unique[i], unique[j] = unique[j], unique[i]
}
}
}
result := 1
unlockedSoFar := 0
for i, comp := range unique {
count := freqMap[comp]
if i == 0 && comp != complexity[0] {
return 0
}
if i > 0 && unlockedSoFar == 0 {
return 0
}
for k := 1; k <= count; k++ {
result = (result * k) % MOD
}
unlockedSoFar += count
}
return result
}
Go-Specific Notes: In Go, we use a map for frequency counts. Sorting is implemented manually with a nested loop for simplicity, though sort.Ints() is preferable. result is accumulated with modular multiplication. Integer overflow is controlled by the modulo operation. The logic mirrors Python, taking care of zero-based indexing and map iteration.
Worked Examples
Example 1: complexity = [1,2,3]
| Step | Unique Complexity | Group Size | Result | Unlocked So Far |
|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 1 |
| 2 | 2 | 1 | 1*1=1 | 2 |
| 3 | 3 | 1 | 1*1=1 | 3 |
Valid permutations: [0,1,2], [0,2,1]. Factorials account for internal order, giving final answer 2.
Example 2: complexity = [3,3,3,4,4,4]
First group 3 does not include the root 0, so return 0.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Sorting unique complexities dominates, O(n) for counting frequencies |
| Space | O(n) | Frequency map and unique list |
The algorithm is efficient enough for n up to $10^5$.
Test Cases
# provided examples
assert Solution().countPermutations([1,2,3]) == 2 # Example 1
assert Solution().countPermutations([3,3,3,4,4,4]) == 0 # Example 2
# boundary cases
assert Solution().countPermutations([1,1]) == 1 # root is unlocked, second same complexity
assert Solution().countPermutations([1,2]) == 1 # strictly increasing
assert Solution().countPermutations([2,1]) == 0 # root not smallest, cannot unlock 1
# larger cases
assert Solution().countPermutations([1]*5) == 120 # factorial(5)
assert Solution().countPermutations([1,2,2,3]) == 4 # permutations respecting rules
| Test | Why |
|---|---|
| [1,2,3] | Simple increasing sequence |
| [3,3,3,4,4,4] | Impossible to unlock first group |
| [1,1] | All equal with root included |
| [1,2] | Minimal increasing case |
| [2,1] | Root not smallest, invalid |
| [1]*5 | All equal, factorial count |
| [1,2,2,3] | Multiple groups, repeated complexities |
Edge Cases
The first important edge case is when all computers have the same complexity. In this case, unlocking is only possible if the root is part of the group, and the number of permutations is simply the factorial of the number We are given an array `complex factorial computation, making it efficient enough for the maximum constraints.