|
| 1 | +## 题目地址(5965. 相同元素的间隔之和) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/intervals-between-identical-elements/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +给你一个下标从 0 开始、由 n 个整数组成的数组 arr 。 |
| 9 | +
|
| 10 | +arr 中两个元素的 间隔 定义为它们下标之间的 绝对差 。更正式地,arr[i] 和 arr[j] 之间的间隔是 |i - j| 。 |
| 11 | +
|
| 12 | +返回一个长度为 n 的数组 intervals ,其中 intervals[i] 是 arr[i] 和 arr 中每个相同元素(与 arr[i] 的值相同)的 间隔之和 。 |
| 13 | +
|
| 14 | +注意:|x| 是 x 的绝对值。 |
| 15 | +
|
| 16 | + |
| 17 | +
|
| 18 | +示例 1: |
| 19 | +
|
| 20 | +输入:arr = [2,1,3,1,2,3,3] |
| 21 | +输出:[4,2,7,2,4,4,5] |
| 22 | +解释: |
| 23 | +- 下标 0 :另一个 2 在下标 4 ,|0 - 4| = 4 |
| 24 | +- 下标 1 :另一个 1 在下标 3 ,|1 - 3| = 2 |
| 25 | +- 下标 2 :另两个 3 在下标 5 和 6 ,|2 - 5| + |2 - 6| = 7 |
| 26 | +- 下标 3 :另一个 1 在下标 1 ,|3 - 1| = 2 |
| 27 | +- 下标 4 :另一个 2 在下标 0 ,|4 - 0| = 4 |
| 28 | +- 下标 5 :另两个 3 在下标 2 和 6 ,|5 - 2| + |5 - 6| = 4 |
| 29 | +- 下标 6 :另两个 3 在下标 2 和 5 ,|6 - 2| + |6 - 5| = 5 |
| 30 | +
|
| 31 | +
|
| 32 | +示例 2: |
| 33 | +
|
| 34 | +输入:arr = [10,5,10,10] |
| 35 | +输出:[5,0,3,4] |
| 36 | +解释: |
| 37 | +- 下标 0 :另两个 10 在下标 2 和 3 ,|0 - 2| + |0 - 3| = 5 |
| 38 | +- 下标 1 :只有这一个 5 在数组中,所以到相同元素的间隔之和是 0 |
| 39 | +- 下标 2 :另两个 10 在下标 0 和 3 ,|2 - 0| + |2 - 3| = 3 |
| 40 | +- 下标 3 :另两个 10 在下标 0 和 2 ,|3 - 0| + |3 - 2| = 4 |
| 41 | +
|
| 42 | +
|
| 43 | + |
| 44 | +
|
| 45 | +提示: |
| 46 | +
|
| 47 | +n == arr.length |
| 48 | +1 <= n <= 10^5 |
| 49 | +1 <= arr[i] <= 10^5 |
| 50 | +``` |
| 51 | + |
| 52 | +## 前置知识 |
| 53 | + |
| 54 | +- 前缀和 |
| 55 | + |
| 56 | +## 公司 |
| 57 | + |
| 58 | +- 暂无 |
| 59 | + |
| 60 | +## 思路 |
| 61 | + |
| 62 | +朴素的思路是 $n^2$ 的暴力枚举,即对于每一个索引 i ,暴力枚举其与数组所有其他索引的间隔,并将其全部加起来即可。 |
| 63 | + |
| 64 | +考虑到数据范围为 $10^5$, 因此上面的思路是不可行的,会超时。我们的思路是优化到至少 $nlogn$。这种数据规模要么优化到 $nlogn$ 要么就是 $n$。 |
| 65 | + |
| 66 | +如果优化到 $n$。对于这种题目容易想到的就是动态规划,单调栈,前缀和。 |
| 67 | + |
| 68 | +首先想到的思路是动态规划。对于每一个索引 i ,我们是否可以借助其他索引的**间隔和**得到答案。 |
| 69 | + |
| 70 | +答案是可以的!这里的其他索引具体来说其实是其他的和 arr[i] 值相等的索引。 不难想到用 dp[i] 表示子数组 arr[:i] 中 i 的间隔和,最终答案就是 dp[n-1]。 |
| 71 | + |
| 72 | +这是一个最初的想法。实际上还有需要细节需要处理。 |
| 73 | + |
| 74 | +- 首先, i 向前看的时候需要看的是和 arr[i] 值相同的已处理好的答案。因此我们的 dp 定义少了一个维度。不妨用 dp[i][x] 表示 子数组 arr[:i] 且值为的 x 的 i 的间隔和,最终答案就是对于数组所有 x dp[n-1][x] 求和。 |
| 75 | +- 其次,如果计算间隔和呢?上面的朴素的思路是对于 i ,枚举所有小于 i 的 j,如果 arr[j] == arr[i], 则加入到间隔和。 |
| 76 | +- 如果优化上一步的计算呢?我们可以利用类似前缀和的技巧来计算。 其中 pre[a] 表示上一次出现的 a 的间隔和。 那么 i 的间隔和就是 `(i - last)*cnt + pre[last] `,其中 last 就是 a 的上一次出现的位置,cnt 是 i 的前面的 a 出现的次数。这提示我们除了维护前缀信息,也要维护 cnt 信息。 pre[a] = (v, c) 表示上一个 a 的位置的前缀间隔和为 v,且前面和 a 相同的数字有 c 个。 |
| 77 | + |
| 78 | +对于每一个 i 仅按照上面的计算会漏掉 i 右侧部分的间隔和。因此我们可以使用相同的技巧,用一个后缀和来解决。 |
| 79 | + |
| 80 | +## 关键点 |
| 81 | + |
| 82 | +- 前缀和 + 后缀和优化时间复杂度 |
| 83 | + |
| 84 | +## 代码 |
| 85 | + |
| 86 | +- 语言支持:Python3 |
| 87 | + |
| 88 | +Python3 Code: |
| 89 | + |
| 90 | +```python |
| 91 | + |
| 92 | +class Solution: |
| 93 | + def getDistances(self, arr: List[int]) -> List[int]: |
| 94 | + ans = [] |
| 95 | + n = len(arr) |
| 96 | + last_map = collections.defaultdict(lambda:-1) |
| 97 | + pre = collections.defaultdict(lambda:(0,0)) |
| 98 | + suf = collections.defaultdict(lambda:(0,0)) |
| 99 | + for i in range(n): |
| 100 | + a = arr[i] |
| 101 | + last = last_map[a] |
| 102 | + v, c = pre[last] |
| 103 | + pre[i] = v + c * (i - last), c + 1 |
| 104 | + last_map[a] = i |
| 105 | + last_map = collections.defaultdict(lambda:len(arr)) |
| 106 | + for i in range(n-1,-1,-1): |
| 107 | + a = arr[i] |
| 108 | + last = last_map[a] |
| 109 | + v, c = suf[last] |
| 110 | + suf[i] = v + c * (last - i), c + 1 |
| 111 | + last_map[a] = i |
| 112 | + for i, a in enumerate(arr): |
| 113 | + ans.append(pre[i][0] + suf[i][0]) |
| 114 | + return ans |
| 115 | + |
| 116 | + |
| 117 | +``` |
| 118 | + |
| 119 | +**复杂度分析** |
| 120 | + |
| 121 | +令 n 为数组长度。 |
| 122 | + |
| 123 | +- 时间复杂度:我们遍历了两次数组,因此时间复杂度为 $O(n)$ |
| 124 | +- 空间复杂度:pre 和 suf 以及 last_map 都和数组不同数字的个数同阶,最差情况数组都是不同的,此时空间复杂度为 $O(n)$ |
| 125 | + |
| 126 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 127 | +
|
| 128 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 129 | + |
| 130 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 131 | + |
| 132 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 133 | + |
| 134 | + |
0 commit comments