这里写自定义目录标题
更多精彩内容
这里是带你游历编程世界的Dashcoding编程社,我是Dash/北航硕士/ICPC区域赛全国排名30+/给你呈现我们眼中的世界!
256题算法特训课,帮你斩获大厂60W年薪offer
原题
OPPO校招真题严格递增三元组
B站动画详解
问题分析
在这个问题中,我们需要找出所有满足条件的三元组 ( i , j , k ) (i, j, k) (i,j,k),使得 1 ≤ i < j < k ≤ n 1 \leq i < j < k \leq n 1≤i<j<k≤n 并且 a i = a k = a j + 1 a_i = a_k = a_j + 1 ai=ak=aj+1。这意味着在三个索引 i , j , k i, j, k i,j,k 中, a i a_i ai 和 a k a_k ak 的值必须相同,而 a j a_j aj 的值比它们小 1。因此,我们需要找到符合这些条件的三元组并统计它们的数量。
由于 n n n 的最大值可以达到 1 0 5 10^5 105, a i a_i ai 的最大值可以达到 1 0 9 10^9 109,我们需要一个高效的算法来完成任务,直接使用三重循环的暴力方法会导致 O ( n 3 ) O(n^3) O(n3) 的复杂度,这在大规模数据下是不可行的。
思路分析
为了解决这个问题,我们可以考虑使用数据结构和遍历的方法来提高效率。我们可以遍历数组,同时维护两个哈希表:
-
bk[x]
:用于记录当前遍历到的元素中,值为 x x x 的元素的出现次数。这将帮助我们统计满足 a i = x a_i = x ai=x 的元素。 -
sum[x+1]
:用于记录当前遍历到的元素中,值为 x + 1 x + 1 x+1 的元素的出现次数。这个哈希表将帮助我们统计满足 a j = x a_j = x aj=x 且 a k = x + 1 a_k = x+1 ak=x+1 的三元组数量。
具体步骤如下:
-
从左到右遍历数组。对于每个元素 x x x:
- 将当前的 b k [ x ] bk[x] bk[x] 值累加到 sum[x+1] 中。这一步表示:已经遍历过的元素中,值为 x x x 的元素可以和未来的某个值为 x + 1 x+1 x+1 的元素形成合法的三元组。
- 更新 bk[x],即记录当前遍历到的值为 x x x 的元素出现的次数。
- 将 sum[x] 中记录的值累加到答案中。这一步表示:当前的 x x x 可以作为合法的三元组中的 a k a_k ak,并且已经找到多少个合法的三元组。
-
最终结果就是所有合法三元组的数量。
算法实现
为了找到所有符合条件的三元组 ( i , j , k ) (i, j, k) (i,j,k),我们采用一种基于哈希表的遍历算法。该算法的核心思想是通过两次哈希表的更新和查询,快速统计出满足条件的三元组数量。
- 初始化两个哈希表:
- bk:用于记录当前遍历到的元素中,值为 x x x 的元素的出现次数。
- sum:用于记录当前遍历到的元素中,值为
x
+
1
x+1
x+1
的元素的出现次数,这将帮助我们在未来找到合法的三元组。
- 遍历数组:
- 对于每个元素 x,首先更新
sum[x+1]
,使其累加bk[x+1]
的值。bk[x+1]
代表的是在当前元素 x 之前出现的值为
x + 1 x+1 x+1 的元素的数量。 - 然后,将
bk[x]
中的值增加 1 1 1,表示当前遍历到的元素 x x x 的数量增加了一个。 - 接下来,累加
sum[x]
到答案ans
中,sum[x]` 中的值代表的是满足 a j = x a_j = x aj=x 且存在 a i = x + 1 a_i = x+1 ai=x+1 的合法三元组的数量。
- 输出结果:遍历完成后,ans 中的值就是满足条件的三元组数量。
代码详解
标准代码程序
C++代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
ll ans, x;
map<int, ll> bk, sum;
int main() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> x;
sum[x + 1] += bk[x + 1]; // 更新sum[x+1],表示值为x+1的元素数量。
bk[x]++; // 记录当前值x的出现次数。
ans += sum[x]; // 统计合法三元组的数量。
}
cout << ans;
}
Java代码
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long ans = 0;
Map<Integer, Long> bk = new HashMap<>();
Map<Integer, Long> sum = new HashMap<>();
for (int i = 1; i <= n; i++) {
int x = sc.nextInt();
sum.put(x + 1, sum.getOrDefault(x + 1, 0L) + bk.getOrDefault(x + 1, 0L));
bk.put(x, bk.getOrDefault(x, 0L) + 1);
ans += sum.getOrDefault(x, 0L);
}
System.out.println(ans);
}
}
Python代码
n = int(input())
a = list(map(int, input().split()))
ans = 0
bk = {}
sum_map = {}
for x in a:
sum_map[x + 1] = sum_map.get(x + 1, 0) + bk.get(x + 1, 0)
bk[x] = bk.get(x, 0) + 1
ans += sum_map.get(x, 0)
print(ans)
Javascript代码
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', function(line) {
if (!this.n) {
this.n = parseInt(line.trim());
} else {
const a = line.trim().split(' ').map(Number);
let ans = 0;
const bk = new Map();
const sum = new Map();
a.forEach(x => {
sum.set(x + 1, (sum.get(x + 1) || 0) + (bk.get(x + 1) || 0));
bk.set(x, (bk.get(x) || 0) + 1);
ans += sum.get(x) || 0;
});
console.log(ans);
rl.close();
}
});
复杂度分析
-
时间复杂度:该算法的时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是数组的长度。我们只需要遍历一次数组,并在每次遍历时进行常数时间的哈希表操作,因此整体复杂度为 O ( n ) O(n) O(n)。
-
空间复杂度:算法使用了两个哈希表
bk
和sum
,每个哈希表的最大空间复杂度为 O ( n ) O(n) O(n)。因此,总的空间复杂度也是 O ( n ) O(n) O(n)。
总结
这个题目要求我们在一个长度为 n n n 的数组中,找到所有符合特定条件的三元组 ( i , j , k ) (i, j, k) (i,j,k),即 1 ≤ i < j < k ≤ n 1 \leq i < j < k \leq n 1≤i<j<k≤n 且 a i = a k = a j + 1 a_i = a_k = a_j + 1 ai=ak=aj+1。为了高效地解决这个问题,我们采用了哈希表和一次遍历的算法来进行处理。
该算法通过两个哈希表来记录和统计符合条件的元素组合情况。我们依次遍历数组中的每个元素,通过更新哈希表中的计数信息,逐步累积得到最终满足条件的三元组数量。这个方法避免了暴力搜索所有可能的三元组,极大地提高了算法的效率。