LeetCode 1223 - Dice Roll Simulation
The problem asks us to calculate the total number of valid sequences generated by rolling a six-sided die n times, with the added constraint that each face i cannot appear more than rollMax[i] consecutive times.
Difficulty: 🔴 Hard
Topics: Array, Dynamic Programming
Solution
Problem Understanding
The problem asks us to calculate the total number of valid sequences generated by rolling a six-sided die n times, with the added constraint that each face i cannot appear more than rollMax[i] consecutive times. Here, rollMax is a 6-element array corresponding to the maximum allowed consecutive appearances for each die face from 1 to 6. The result should be returned modulo 10^9 + 7 due to potential large numbers.
In other words, if we roll the die n times, we want to count all sequences of length n where the constraints from rollMax are satisfied. The input n can be as large as 5000, and each rollMax[i] is at most 15. This implies that a brute-force enumeration of all sequences is computationally infeasible because the number of possible sequences grows exponentially (6^n).
Important edge cases include scenarios where rollMax is all 1s (sequences with immediate repetition are disallowed) or when n is small (like 1 or 2), which can be solved almost trivially. The constraints guarantee that rollMax is always valid (positive integers) and the array always has exactly 6 elements, so we do not need to handle malformed input.
Approaches
The brute-force approach would be to generate all 6^n sequences recursively or iteratively and check each sequence against the rollMax constraints. While this produces the correct result, it is completely impractical for n = 5000 because of exponential growth in both time and memory.
The dynamic programming approach is the key observation. We notice that at any step, the number of valid sequences depends on the last number rolled and how many times it has consecutively appeared. Thus, we can define a DP state dp[i][j][k], where i is the current roll index, j is the last die face rolled, and k is the number of consecutive times j has been rolled. Using this state, we can incrementally build solutions by considering all valid previous states. By carefully updating the DP array and using modulo arithmetic, we can efficiently compute the total count for n rolls without enumerating all sequences.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(6^n) | O(n) | Enumerates all sequences and checks validity |
| Dynamic Programming | O(n * 6 * max(rollMax)) | O(n * 6 * max(rollMax)) | Uses DP over rolls, last number, and consecutive count to compute valid sequences |
Algorithm Walkthrough
- Initialize a 2D DP array
dp[j][k]wherejrepresents the die face (0-5) andkrepresents consecutive occurrences ofj.dp[j][k]stores the number of sequences of the current length ending with facejappearing consecutivelyktimes. - Set the base case for the first roll: each face
jcan appear once, sodp[j][1] = 1for allj. - Iterate from roll
2ton. For each new roll, create a new DP arraynew_dpto store updated counts. - For each die face
j, consider all possible consecutive countsk(from 1 torollMax[j]). - For each possible previous die face
i, ifi == j, we can only extend sequences ending withiif the consecutive countk - 1is valid. Otherwise, ifi != j, we can start a new sequence withjby summing all sequences ending withiregardless of their consecutive count. - Update
new_dp[j][k]with the calculated counts modulo10^9 + 7. - After processing all faces for this roll, replace
dpwithnew_dp. - After all
nrolls, sum over alldp[j][k]to get the total number of valid sequences.
Why it works: The DP approach guarantees correctness because it tracks all necessary state: the last face rolled and how many times it has appeared consecutively. This ensures that no sequence violates the rollMax constraints and all valid sequences are counted exactly once.
Python Solution
from typing import List
class Solution:
def dieSimulator(self, n: int, rollMax: List[int]) -> int:
MOD = 10**9 + 7
max_roll = max(rollMax)
dp = [[0] * (max_roll + 1) for _ in range(6)]
for j in range(6):
dp[j][1] = 1
for roll in range(1, n):
new_dp = [[0] * (max_roll + 1) for _ in range(6)]
for j in range(6):
for k in range(1, rollMax[j] + 1):
if k == 1:
new_dp[j][k] = sum(dp[i][c] for i in range(6) if i != j for c in range(1, rollMax[i] + 1)) % MOD
else:
new_dp[j][k] = dp[j][k - 1]
dp = new_dp
return sum(dp[j][k] for j in range(6) for k in range(1, rollMax[j] + 1)) % MOD
The Python solution initializes a DP array with dimensions [6][max_roll+1] to store counts. The first roll is seeded with 1 for each face. For subsequent rolls, a new DP array is created to hold updated counts. Sequences are extended either by incrementing the consecutive count for the same face or by starting a new count for a different face. The modulo operation ensures no integer overflow. Finally, the sum over all states yields the total number of valid sequences.
Go Solution
func dieSimulator(n int, rollMax []int) int {
const MOD = 1e9 + 7
maxRoll := 0
for _, v := range rollMax {
if v > maxRoll {
maxRoll = v
}
}
dp := make([][]int, 6)
for i := 0; i < 6; i++ {
dp[i] = make([]int, maxRoll+1)
dp[i][1] = 1
}
for roll := 1; roll < n; roll++ {
newDp := make([][]int, 6)
for i := 0; i < 6; i++ {
newDp[i] = make([]int, maxRoll+1)
}
for j := 0; j < 6; j++ {
for k := 1; k <= rollMax[j]; k++ {
if k == 1 {
sum := 0
for i := 0; i < 6; i++ {
if i == j {
continue
}
for c := 1; c <= rollMax[i]; c++ {
sum = (sum + dp[i][c]) % MOD
}
}
newDp[j][k] = sum
} else {
newDp[j][k] = dp[j][k-1]
}
}
}
dp = newDp
}
result := 0
for j := 0; j < 6; j++ {
for k := 1; k <= rollMax[j]; k++ {
result = (result + dp[j][k]) % MOD
}
}
return result
}
In Go, slices are used for the DP array. Initialization and updating follow the same logic as in Python. Go-specific considerations include explicit slice creation and modulo arithmetic to prevent overflow.
Worked Examples
Example 1: n=2, rollMax=[1,1,2,2,2,3]
- First roll:
dp[j][1] = 1for allj. - Second roll: compute
new_dp. Faces 1 and 2 cannot appear consecutively (k=2 not allowed), so only sequences wherej != previousare counted. - Total valid sequences = 34.
Example 2: n=2, rollMax=[1,1,1,1,1,1]
- First roll: each face once.
- Second roll: consecutive repeats disallowed, only sequences with different faces counted.
- Total = 30.
Example 3: n=3, rollMax=[1,1,1,2,2,3]
- Iterate rolls, updating DP for consecutive constraints.
- Sequences extending previous faces respect max consecutive counts.
- Total = 181.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * 6 * max(rollMax)) | For each roll, we iterate over 6 faces and at most rollMax[i] consecutive counts |
| Space | O(6 * max(rollMax)) | Only two DP arrays of size 6 * maxRoll are stored at any time |
Because `max