LeetCode 3700 - Number of ZigZag Arrays II
The problem asks us to count the number of arrays of length n where each element is in the range [l, r], no two consecutive elements are equal, and no three consecutive elements are strictly increasing or strictly decreasing. These arrays are termed ZigZag arrays.
Difficulty: 🔴 Hard
Topics: Math, Dynamic Programming
Solution
Problem Understanding
The problem asks us to count the number of arrays of length n where each element is in the range [l, r], no two consecutive elements are equal, and no three consecutive elements are strictly increasing or strictly decreasing. These arrays are termed ZigZag arrays.
In other words, for an array A of length n:
- For all
i,l <= A[i] <= r. - For all
iin[0, n-2],A[i] != A[i+1]. - For all
iin[0, n-3], the triplet(A[i], A[i+1], A[i+2])is not strictly increasing nor strictly decreasing.
The input n can be very large, up to 10^9, which rules out brute-force enumeration of all possible arrays. The range [l, r] is small (<=75), which suggests a dynamic programming approach over the values rather than the positions is feasible. The answer must be returned modulo 10^9 + 7.
Edge cases to consider include l and r being consecutive (e.g., r-l=1), where choices are severely limited, and small n values where patterns might not even reach three elements.
Approaches
The brute-force approach would generate all possible sequences of length n in the range [l, r] and filter them according to the ZigZag rules. This would involve O((r-l+1)^n) sequences, which is completely infeasible for large n. Even recursive backtracking with memoization is impractical due to n up to 10^9.
The optimal approach leverages the observation that the problem has recurrence and symmetry properties. Specifically, the constraints only depend on the last two elements of the array. Therefore, we can model the problem as a matrix exponentiation problem. Let dp[a][b] represent the number of valid sequences ending with the last two elements being (a, b). Then, to compute the next element c, we only need to check c != b and that (a, b, c) does not form a strictly increasing or decreasing sequence. This forms a transition matrix, which can be exponentiated efficiently to compute sequences of length n.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O((r-l+1)^n) | O(n) | Generate all sequences and filter; infeasible for large n |
| Optimal (DP + Matrix Exponentiation) | O((r-l+1)^3 * log n) | O((r-l+1)^2) | Track last two elements; use matrix exponentiation to handle large n efficiently |
Algorithm Walkthrough
- Define
m = r - l + 1as the number of possible values in the array. Each value can be mapped to0..m-1for simplicity. - Construct all valid transitions between pairs
(a, b)and the next valuec. A transition is valid ifc != band(a, b, c)is not strictly increasing nor decreasing. - Encode all pairs
(a, b)as indices from0tom^2-1. Construct a transition matrixTof sizem^2 x m^2whereT[i][j] = 1if pairjcan follow pairi. - The initial vector
vrepresents all valid sequences of length 2. Since any two distinct numbers(a, b)are allowed, setv[i] = 1ifa != b. - Compute
T^(n-2)using fast matrix exponentiation. Multiply it byvto get the number of sequences of lengthn. - Sum all entries in the resulting vector modulo
10^9 + 7to get the total number of ZigZag arrays.
Why it works: The key invariant is that the constraints only depend on the last two elements. By encoding all possible pairs and transitions, we guarantee that all sequences counted satisfy the ZigZag conditions. Matrix exponentiation allows efficient computation for large n.
Python Solution
class Solution:
MOD = 10**9 + 7
def zigZagArrays(self, n: int, l: int, r: int) -> int:
m = r - l + 1
size = m * m
def index(a, b):
return a * m + b
# Build transition matrix
T = [[0] * size for _ in range(size)]
for a in range(m):
for b in range(m):
if a == b:
continue
for c in range(m):
if c == b:
continue
if (a < b < c) or (a > b > c):
continue
T[index(a, b)][index(b, c)] = 1
# Initial vector for sequences of length 2
vec = [0] * size
for a in range(m):
for b in range(m):
if a != b:
vec[index(a, b)] = 1
def mat_mult(A, B):
n = len(A)
m = len(B[0])
p = len(B)
C = [[0]*m for _ in range(n)]
for i in range(n):
for k in range(p):
if A[i][k] == 0:
continue
for j in range(m):
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % self.MOD
return C
def mat_pow(mat, power):
n = len(mat)
res = [[1 if i==j else 0 for j in range(n)] for i in range(n)]
while power:
if power % 2:
res = mat_mult(res, mat)
mat = mat_mult(mat, mat)
power //= 2
return res
# Exponentiate matrix to length n-2
if n == 2:
return sum(vec) % self.MOD
Tn = mat_pow(T, n-2)
# Multiply Tn with initial vector
res = 0
for i in range(size):
for j in range(size):
res = (res + Tn[i][j] * vec[j]) % self.MOD
return res
The Python code first encodes the transitions for all valid pairs. It uses matrix multiplication and exponentiation to scale for large n. The final multiplication with the initial vector counts all sequences of length n.
Go Solution
package main
func zigZagArrays(n int, l int, r int) int {
const MOD = 1_000_000_007
m := r - l + 1
size := m * m
index := func(a, b int) int {
return a*m + b
}
// Build transition matrix
T := make([][]int, size)
for i := range T {
T[i] = make([]int, size)
}
for a := 0; a < m; a++ {
for b := 0; b < m; b++ {
if a == b {
continue
}
for c := 0; c < m; c++ {
if c == b {
continue
}
if (a < b && b < c) || (a > b && b > c) {
continue
}
T[index(a, b)][index(b, c)] = 1
}
}
}
// Initial vector
vec := make([]int, size)
for a := 0; a < m; a++ {
for b := 0; b < m; b++ {
if a != b {
vec[index(a, b)] = 1
}
}
}
matMult := func(A, B [][]int) [][]int {
n := len(A)
m := len(B[0])
p := len(B)
C := make([][]int, n)
for i := 0; i < n; i++ {
C[i] = make([]int, m)
for k := 0; k < p; k++ {
if A[i][k] == 0 {
continue
}
for j := 0; j < m; j++ {
C[i][j] = (C[i][j] + A[i][k]*B[k][j]) % MOD
}
}
}
return C
}
matPow := func(mat [][]int, power int) [][]int {
n := len(mat)
res := make([][]int, n)
for i := 0; i < n; i++ {
res[i] = make([]int, n)
res[i][i] = 1
}
for power > 0 {
if power%2 == 1 {
res = matMult(res, mat)
}
mat = matMult(mat, mat)
power /= 2
}
return res
}
if n == 2 {
sum :=