跳至主要內容

1534. 统计好三元组

ITCharge大约 3 分钟

1534. 统计好三元组open in new window

  • 标签:数组、枚举
  • 难度:简单

题目链接

题目大意

描述:给定一个整数数组 arrarr,以及 aabbcc 三个整数。

要求:统计其中好三元组的数量。

说明

  • 好三元组:如果三元组(arr[i]arr[i]arr[j]arr[j]arr[k]arr[k])满足下列全部条件,则认为它是一个好三元组。

    • 0i<j<k<arr.length0 \le i < j < k < arr.length
    • arr[i]arr[j]a| arr[i] - arr[j] | \le a
    • arr[j]arr[k]b| arr[j] - arr[k] | \le b
    • arr[i]arr[k]c| arr[i] - arr[k] | \le c
  • 3arr.length1003 \le arr.length \le 100

  • 0arr[i]10000 \le arr[i] \le 1000

  • 0a,b,c10000 \le a, b, c \le 1000

示例

  • 示例 1:
输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出:4
解释:一共有 4 个好三元组:[(3,0,1), (3,0,1), (3,1,1), (0,1,1)]
  • 示例 2:
输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出:0
解释:不存在满足所有条件的三元组。

解题思路

思路 1:枚举

  • 使用三重循环依次枚举所有的 (i,j,k)(i, j, k),判断对应 arr[i]arr[i]arr[j]arr[j]arr[k]arr[k] 是否满足条件。
  • 然后统计出所有满足条件的三元组的数量。

思路 1:代码

class Solution:
    def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
        size = len(arr)
        ans = 0
        
        for i in range(size):
            for j in range(i + 1, size):
                for k in range(j + 1, size):
                    if abs(arr[i] - arr[j]) <= a and abs(arr[j] - arr[k]) <= b and abs(arr[i] - arr[k]) <= c:
                        ans += 1
        
        return ans

思路 1:复杂度分析

  • 时间复杂度O(n3)O(n^3),其中 nn 是数组 arrarr 的长度。
  • 空间复杂度O(1)O(1)

思路 2:枚举优化 + 前缀和

我们可以先通过二重循环遍历二元组 (j,k)(j, k),找出所有满足 arr[j]arr[k]b| arr[j] - arr[k] | \le b 的二元组。

然后在 arr[j]arr[k]b| arr[j] - arr[k] | \le b 的条件下,我们需要找到满足以下要求的 arr[i]arr[i] 数量:

  1. i<ji < j
  2. arr[i]arr[j]a| arr[i] - arr[j] | \le a
  3. arr[i]arr[k]c| arr[i] - arr[k] | \le c
  4. 0arr[i]10000 \le arr[i] \le 1000

其中 2233 去除绝对值之后可变为:

  1. arr[j]aarr[i]arr[j]+aarr[j] - a \le arr[i] \le arr[j] + a
  2. arr[k]carr[i]arr[k]+carr[k] - c \le arr[i] \le arr[k] + c

将这两个条件再结合第 44 个条件综合一下就变为:max(0,arr[j]a,arr[k]c)arr[i]min(arr[j]+a,arr[k]+c,1000)max(0, arr[j] - a, arr[k] - c) \le arr[i] \le min(arr[j] + a, arr[k] + c, 1000)

假如定义 left=max(0,arr[j]a,arr[k]c)left = max(0, arr[j] - a, arr[k] - c)right=min(arr[j]+a,arr[k]+c,1000)right = min(arr[j] + a, arr[k] + c, 1000)

现在问题就转变了如何快速获取在值域区间 [left,right][left, right] 中,有多少个 arr[i]arr[i]

我们可以利用前缀和数组,先计算出 [0,1000][0, 1000] 范围中,满足 arr[i]<numarr[i] < num 的元素个数,即为 prefixcnts[num]prefix\underline{\hspace{0.5em}}cnts[num]

然后对于区间 [left,right][left, right],通过 prefixcnts[right]prefixcnts[left1]prefix\underline{\hspace{0.5em}}cnts[right] - prefix\underline{\hspace{0.5em}}cnts[left - 1] 即可快速求解出区间 [left,right][left, right]arr[i]arr[i] 的个数。

因为 i<j<ki < j < k,所以我们可以在每次 jj 向右移动一位的时候,更新 arr[j]arr[j] 对应的前缀和数组,保证枚举到 jj 时,prefixcntsprefix\underline{\hspace{0.5em}}cnts 存储对应元素值的个数足够正确。

思路 2:代码

class Solution:
    def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
        size = len(arr)
        ans = 0
        prefix_cnts = [0 for _ in range(1010)]

        for j in range(size):
            for k in range(j + 1, size):
                if abs(arr[j] - arr[k]) <= b:
                    left_j, right_j = arr[j] - a, arr[j] + a
                    left_k, right_k = arr[k] - c, arr[k] + c
                    left, right = max(0, left_j, left_k), min(1000, right_j, right_k)
                    if left <= right:
                        if left == 0:
                            ans += prefix_cnts[right]
                        else:
                            ans += prefix_cnts[right] - prefix_cnts[left - 1]

            for k in range(arr[j], 1001):
                prefix_cnts[k] += 1
        
        return ans

思路 2:复杂度分析

  • 时间复杂度O(n2+n×S)O(n^2 + n \times S),其中 nn 是数组 arrarr 的长度,SS 为数组的值域上限。
  • 空间复杂度O(S)O(S)