{x}
blog image

K Inverse Pairs Array

For an integer array nums, an inverse pair is a pair of integers [i, j] where 0 <= i < j < nums.length and nums[i] > nums[j].

Given two integers n and k, return the number of different arrays consisting of numbers from 1 to n such that there are exactly k inverse pairs. Since the answer can be huge, return it modulo 109 + 7.

 

Example 1:

Input: n = 3, k = 0
Output: 1
Explanation: Only the array [1,2,3] which consists of numbers from 1 to 3 has exactly 0 inverse pairs.

Example 2:

Input: n = 3, k = 1
Output: 2
Explanation: The array [1,3,2] and [2,1,3] have exactly 1 inverse pair.

 

Constraints:

  • 1 <= n <= 1000
  • 0 <= k <= 1000

Solution Explanation: K Inverse Pairs Array

This problem asks to find the number of different arrays of length n containing numbers from 1 to n with exactly k inverse pairs. An inverse pair is a pair (i, j) where 0 <= i < j < n and nums[i] > nums[j]. Since the answer can be very large, we need to compute the result modulo 109 + 7.

The most efficient solution uses dynamic programming along with prefix sum optimization.

Approach: Dynamic Programming with Prefix Sum Optimization

  1. State Definition: Let dp[i][j] represent the number of arrays of length i with exactly j inverse pairs.

  2. Base Case: dp[0][0] = 1 (An empty array has 0 inverse pairs). All other dp[i][j] are initially 0.

  3. Transition: This is the core of the dynamic programming solution. Consider adding the number i+1 to an existing array of length i with j inverse pairs. When we insert i+1 into the array, it creates additional inverse pairs depending on its position.

    • If we insert i+1 at position 0, we add i inverse pairs.
    • If we insert i+1 at position 1, we add i-1 inverse pairs.
    • ...
    • If we insert i+1 at position i, we add 0 inverse pairs.

    Therefore, the number of arrays of length i+1 with j inverse pairs is the sum of the number of arrays of length i with j - (i - k) inverse pairs, where k ranges from 0 to i. This leads to the recurrence relation:

    dp[i+1][j] = Σ dp[i][j - (i - k)] for k from 0 to i (with appropriate boundary conditions to handle negative indices and cases where j is smaller than the subtracted value).

  4. Prefix Sum Optimization: The direct computation of the above summation has a time complexity of O(n*k^2). This can be improved using prefix sums. Let prefixSum[i][j] = Σ dp[i][x] for x from 0 to j. Then we can rewrite the recurrence as:

    dp[i+1][j] = prefixSum[i][j] - prefixSum[i][max(0, j - i -1)]

    This reduces the computation from O(nk) to O(nk).

  5. Space Optimization: Notice that dp[i+1][j] only depends on the ith row. Therefore, we can reduce the space complexity from O(n*k) to O(k) by using a single array for dp and updating it iteratively.

Code (Python):

def kInversePairs(n: int, k: int) -> int:
    mod = 10**9 + 7
    dp = [0] * (k + 1)
    dp[0] = 1
    prefixSum = [0] * (k + 2)
    prefixSum[1] = 1
 
    for i in range(1, n + 1):
        new_dp = [0] * (k + 1)
        new_prefixSum = [0] * (k + 2)
 
        for j in range(k + 1):
            new_dp[j] = (prefixSum[j + 1] - prefixSum[max(0, j - i + 1)] + mod) % mod
 
        new_prefixSum[1] = new_dp[0]
        for j in range(1, k + 1):
             new_prefixSum[j + 1] = (new_prefixSum[j] + new_dp[j]) % mod
 
        dp = new_dp
        prefixSum = new_prefixSum
    return dp[k]
 
 

Time and Space Complexity:

  • Time Complexity: O(n*k) due to the nested loops in the dynamic programming calculation. The prefix sum calculation is O(k) per row, and we perform this for n rows.

  • Space Complexity: O(k) because we are using only one array dp of size k+1 to store the dynamic programming results, and a single array for the prefix sums.

The provided code implements this optimized dynamic programming solution. Other languages (Java, C++, Go, Typescript) can implement the same approach with similar time and space complexities. Remember to handle modulo operations correctly to avoid integer overflow.