LeetCode Ts-3699. Number of ZigZag Arrays I

    CodingLeetCode Js-DP

    You are given three integers n, l, and r.
    A ZigZag array of length n is defined as follows:
    Each element lies in the range [l, r].
    No two adjacent elements are equal.
    No three consecutive elements form a strictly increasing or strictly decreasing sequence.
    Return the total number of valid ZigZag arrays.
    Since the answer may be large, return it modulo 109 + 7.
    A sequence is said to be strictly increasing if each element is strictly greater than its previous one (if exists).
    A sequence is said to be strictly decreasing if each element is strictly smaller than its previous one (if exists).

    你給予三個整數分別是 n, l, and r。
    一個長度為 n 的鋸齒狀( /\ )陣列的定義如下:
    - 每個元素位於 [l, r] 範圍內。
    - 任何兩個相鄰的元素都不相等。
    - 任何三個連續元素都不能構成嚴格地遞增或嚴格地遞減。
    - 回傳有效的鋸齒狀( /\ )陣列總數。
    由於答案可能非常大,請回傳答案的 modulo 10^9 + 7 => 1,000,000,007。
    如果陣列中的每個元素都嚴格大於前一個元素,則稱該陣列為嚴格地遞增。
    如果陣列中的每個元素都嚴格小於前一個元素,則稱該陣列為嚴格地遞減。
    

    Example 1:

    Input: n = 3, l = 4, r = 5
    Output: 2
    Explanation:
    There are only 2 valid ZigZag arrays of length n = 3 using values in the range [4, 5]:
    [4, 5, 4]
    [5, 4, 5]​​​​​​​
    

    Example 2:

    Input: n = 3, l = 1, r = 3
    Output: 10
    Explanation:
    There are 10 valid ZigZag arrays of length n = 3 using values in the range [1, 3]:
    [1, 2, 1], [1, 3, 1], [1, 3, 2]
    [2, 1, 2], [2, 1, 3], [2, 3, 1], [2, 3, 2]
    [3, 1, 2], [3, 1, 3], [3, 2, 3]
    All arrays meet the ZigZag conditions.
    

    Solution:
    這題 3 <= n <= 2000 的情況下,答案有可能超大,所以提供 MOD 10^9 + 7(1_000_000_007),以此避免數值溢位。 我們可以挑選的數值必須介於 r - l + 1 之間, 同時我們可以透過 new Array().fill(1) 的方式把長度 n 的陣列先放入 1 數值,也就是我們需要知道有多少組合的初始值定義 dpLast。 接著我們開始遍歷這個長度為 3 的迴圈,索引從 0 到 2,對應三個數值。 在這過程之中我們需要計算前綴和,目的是新元素比前一個元素小的情況下有多少種組合,同時 % MOD 保證數字不溢位。

    dpLast [ 1, 1 ]
    
    length 2
    – count 1, prefixSums [ 0, 1 ]
    – count 1, prefixSums [ 0, 1, 2 ]
    
    length 3
    – count 1, prefixSums [ 0, 1 ]
    – count 0, prefixSums [ 0, 1, 1 ]
    

    再來我們要跑一遍倒序的陣列,並且只取和 dpLast 一樣長的元素數量,確保新的狀態數組長度與可選值數量一致。

    dpLast [ 1, 1 ]
    
    length 2
    - index 1, dpNext [ 1 ]
    - index 0, dpNext [ 1, 0 ]
    
    length 3
    - index 1, dpNext [ 1 ]
    - index 0, dpNext [ 1, 0 ]
    

    遍歷過程中,我們需要把當前這一輪的結果當作下一次輪的計算基礎。
    最後,把 dpLast 的所有值加總,再乘以 2。這是因為鋸齒狀陣列具有對稱性,需要同時計算「遞增」與「遞減」兩種排列情況。

    Code 1: BigO(n * (r – l + 1)

    function zigZagArrays(n: number, l: number, r: number): number {
        const MOD = 1_000_000_007;
        const valueCount = r - l + 1;
     
        let dpLast: number[] = new Array(valueCount).fill(1); 
        for (let length = 2; length <= n; length++) {
            const prefixSums: number[] = [0];
            for (let count of dpLast) {            
                prefixSums.push((prefixSums[prefixSums.length - 1] + count) % MOD); 
            }
     
            const dpNext: number[] = [];
            for (let index = valueCount - 1; index >= 0; index--) {            
                dpNext.push(prefixSums[index]);          
            }
            dpLast = dpNext;
        }
    
        const total = dpLast.reduce((sum, count) => (sum + count) % MOD, 0);
        return (total * 2) % MOD;
    };
    

    FlowChart:
    Example 1

    Input: n = 3, l = 4, r = 5
    
    valueCount = r - l + 1 = 5 - 4 + 1 = 2 // (4 or 5)
    dpLast [ 1, 1 ] 
    
    length 2
    dpLast [ 1, 0 ] // [5, 4]
    
    length 3
    dpLast [ 1, 0 ] // [5, 4, 5]
    
    total = 1 
    (total * 2) % MOD = 2 // [5, 4, 5] and [4, 5, 4]
    

    Example 2

    Input: n = 3, l = 1, r = 3
    
    valueCount = r - l + 1 = 3 - 1 + 1 = 3 // 1 or 2 or 3
    dpLast [ 1, 1, 1 ] // [1, 2, 3]
    
    length 2
    dpLast [ 2, 1, 0 ] // [2, 1], [3, 1]
    
    length 3
    dpLast [ 3, 2, 0 ] 
    // [2, 3 ,1], [3, 2, 1], [3, 1, 2]
    // [1, 3, 2], [2, 1, 2]
    
    total = 5
    (total * 2) % MOD = 10
    // [1,2,1], [1,3,1], [1,3,2]
    // [2,1,2], [2,1,3], [2,3,1], [2,3,2]
    // [3,1,2], [3,1,3], [3,2,3]