Skip to content

Commit 138788e

Browse files
author
robot
committed
feat: weekly
1 parent 6997ad7 commit 138788e

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
416416
- [2008. 出租车的最大盈利](./problems/2008.maximum-earnings-from-taxi.md)
417417
- [5935. 适合打劫银行的日子](./problems/5935.find-good-days-to-rob-the-bank.md)
418418
- [5936. 引爆最多的炸弹](./problems/5936.detonate-the-maximum-bombs.md)
419+
- [5965. 相同元素的间隔之和](./problems/5965.intervals-between-identical-elements.md)
419420

420421
### 困难难度题目合集
421422

SUMMARY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@
263263
- [1904. 你完成的完整对局数](./problems/1904.the-number-of-full-rounds-you-have-played.md)
264264
- [1906. 查询差绝对值的最小值](./problems/1906.minimum-absolute-difference-queries.md)
265265
- [2008. 出租车的最大盈利](./problems/2008.maximum-earnings-from-taxi.md)
266+
- [5935. 适合打劫银行的日子](./problems/5935.find-good-days-to-rob-the-bank.md)
267+
- [5936. 引爆最多的炸弹](./problems/5936.detonate-the-maximum-bombs.md)
268+
- [5965. 相同元素的间隔之和](./problems/5965.intervals-between-identical-elements.md)
266269

267270
- [第六章 - 高频考题(困难)](collections/hard.md)
268271

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)