Leetcode 2179. Count Good Triplets in an Array
Table of Contents
Problem Informations
- Problem Index: 2179
- Problem Link: Count Good Triplets in an Array
- Topics: Array, Binary Search, Divide and Conquer, Binary Indexed Tree, Segment Tree, Merge Sort, Ordered Set
Problem Description
You are given two 0-indexed arrays nums1
and nums2
of length $n$, both of which are permutations of $[0, 1, \ldots, n - 1]$.
A good triplet is a set of 3
distinct values which are present in increasing order by position both in nums1
and nums2
. In other words, if we consider $pos1_v$ as the index of the value $v$ in nums1
and $pos2_v$ as the index of the value $v$ in nums2
, then a good triplet will be a set $(x, y, z)$ where $0 \leq x, y, z \leq n - 1$, such that $pos1_x < pos1_y < pos1_z$ and $pos2_x < pos2_y < pos2_z$.
Return the total number of good triplets.
Example 1:
Input: nums1 = [2,0,1,3], nums2 = [0,1,2,3]
Output: 1
Explanation:
There are 4 triplets (x,y,z) such that $pos1_x < pos1_y < pos1_z$. They are (2,0,1), (2,0,3), (2,1,3), and (0,1,3).
Out of those triplets, only the triplet (0,1,3) satisfies $pos2_x < pos2_y < pos2_z$. Hence, there is only 1 good triplet.
Example 2:
Input: nums1 = [4,0,1,3,2], nums2 = [4,1,0,2,3]
Output: 4
Explanation: The 4 good triplets are (4,0,3), (4,0,2), (4,1,3), and (4,1,2).
Constraints:
- $n == nums1.length == nums2.length$
- $3 \leq n \leq 10^5$
- $0 \leq nums1[i], nums2[i] \leq n - 1$
nums1
andnums2
are permutations of $[0, 1, \ldots, n - 1]$.
Intuition
The problem requires us to determine the number of “good triplets” in two permutations of a sequence. A good triplet consists of three elements that appear in a specific increasing order in both permutations. Given the constraint that nums1
and nums2
are permutations of the same set, we can leverage this property to track relative positions efficiently, enabling us to identify valid triplets without needing to directly compare them in an $O(n^3)$ fashion.
Approach
The given solution utilizes a data structure known as a Fenwick Tree (also called a Binary Indexed Tree), which is effective for querying prefix sums over an array and performing point updates in logarithmic time. This is particularly suitable for addressing the counting of inversions or, in our case, counting valid triplet configurations.
Mapping Positions:
First, a mapping of the positions of elements innums2
is created. This is stored in an arraypos2
wherepos2[val]
gives the position ofval
in thenums2
array. This mapping helps in transition from the order innums1
to the order innums2
.Index Mapping from
nums1
tonums2
:
Using thepos2
mapping, the elements innums1
are transformed into their respective indices innums2
, resulting inindexMapping
. Here,indexMapping[i]
gives the position of the elementnums1[i]
innums2
.Fenwick Tree Initialization:
A Fenwick Tree is initialized to keep track of the prefix sums efficiently. This structure supports logarithmic updates and prefix queries.Counting Good Triplets:
Iterate through each position inindexMapping
, treating this as the middle elementmid
of a potential triplet:- Calculate
left
: the number of indices smaller than the currentmid
that hold a smaller value, which can be found using a prefix sum query up toindexMapping[mid]
in the Fenwick Tree. - Calculate
right
: the potentiallarger
values to the right of the currentmid
, computed as the remaining indices to the right minus those counted asleft
. - Update the Fenwick Tree to include the current position.
- The number of good triplets that can end at the current
mid
is given by multiplyingleft
andright
because these are independent choices.
- Calculate
Complexity Consideration:
Each update and query operation in the Fenwick Tree is $O(\log n)$, and the overall approach involves a single pass through the array, making the complexity approximately $O(n \log n)$. This is efficient given the problem’s constraints.
By following these steps, the algorithm effectively counts the number of good triplets, adhering to both the conditions specified in the problem statement.
Code
C++
class FenwickTree {
private:
vector<int> tree;
// Return the lowest bit (1) of the number
int lowbit(int num) {
return num & -num;
}
public:
// Initialize the Fenwick Tree with given size
FenwickTree(int size) {
tree = vector(size + 1, 0);
}
// Update the Fenwick Tree at a specific index with a delta value
void update(int idx, int delta) {
idx++;
while (idx < tree.size()) {
tree[idx] += delta;
idx += lowbit(idx);
}
}
// Query the prefix sum up to a given index in the Fenwick Tree
int query(int idx) {
idx++;
int sum = 0;
while (idx > 0) {
sum += tree[idx];
idx -= lowbit(idx);
}
return sum;
}
};
class Solution {
public:
long long goodTriplets(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
long long totalGoodTriplets = 0;
vector<int> pos2(n), indexMapping(n);
// Record positions of each element in nums2
for (int i = 0; i < n; i++) {
pos2[nums2[i]] = i;
}
// Map nums1 elements' positions in nums2
for (int i = 0; i < n; i++) {
indexMapping[i] = pos2[nums1[i]];
}
FenwickTree tree(n);
// Calculate good triplets
for (int mid = 0; mid < n; mid++) {
int pos = indexMapping[mid];
int left = tree.query(pos);
int right = (n - pos - 1) - (mid - left);
// Update Fenwick Tree for the current mid position
tree.update(pos, 1);
// Calculate number of good triplets based on the current left and right values
totalGoodTriplets += static_cast<long long>(left) * right;
}
return totalGoodTriplets;
}
};
Python
class FenwickTree:
def __init__(self, size):
# Initialize the Fenwick Tree with given size
self.tree = [0] * (size + 1)
def lowbit(self, num):
# Return the lowest bit (1) of the number
return num & -num
def update(self, idx, delta):
# Update the Fenwick Tree at a specific index with a delta value
idx += 1
while idx < len(self.tree):
self.tree[idx] += delta
idx += self.lowbit(idx)
def query(self, idx):
# Query the prefix sum up to a given index in the Fenwick Tree
idx += 1
sum = 0
while idx > 0:
sum += self.tree[idx]
idx -= self.lowbit(idx)
return sum
class Solution:
def goodTriplets(self, nums1, nums2):
n = len(nums1)
totalGoodTriplets = 0
pos2 = [0] * n
indexMapping = [0] * n
# Record positions of each element in nums2
for i in range(n):
pos2[nums2[i]] = i
# Map nums1 elements' positions in nums2
for i in range(n):
indexMapping[i] = pos2[nums1[i]]
tree = FenwickTree(n)
# Calculate good triplets
for mid in range(n):
pos = indexMapping[mid]
left = tree.query(pos)
right = (n - pos - 1) - (mid - left)
# Update Fenwick Tree for the current mid position
tree.update(pos, 1)
# Calculate number of good triplets based on the current left and right values
totalGoodTriplets += left * right
return totalGoodTriplets
Complexity
Time complexity: $O(n \log n)$
The algorithm primarily consists of three main components which determine its time complexity:
Initial Setup: It takes $O(n)$ time to fill
pos2
andindexMapping
arrays, where $n$ is the length ofnums1
(ornums2
).Fenwick Tree Updates and Queries: The main loop iterates over each element in
indexMapping
, and within each iteration, it performs a query and an update operation on the Fenwick Tree. Both the update and query operations on a Fenwick Tree take $O(\log n)$ time. Since this loop runs $n$ times, the overall time complexity for this part is $O(n \log n)$.
Combining these components, the dominant term is $O(n \log n)$ due to the Fenwick Tree operations, which makes the overall time complexity $O(n \log n)$.
Space complexity: $O(n)$
The space complexity is primarily determined by the additional storage used:
Fenwick Tree Storage: The Fenwick Tree itself maintains an array
tree
of size $n+1$.Auxiliary Arrays: Two auxiliary arrays
pos2
andindexMapping
are each of size $n$.
Therefore, the overall space complexity is $O(n)$, as the size of the Fenwick Tree and the auxiliary arrays dominate the space usage.
Disclaimer: All reference materials on this website are sourced from the internet and are intended for learning purposes only. If you believe any content infringes upon your rights, please contact me at csnote.cc@gmail.com, and I will remove the relevant content promptly.
Feedback Welcome: If you notice any errors or areas for improvement in the articles, I warmly welcome your feedback and corrections. Your input will help this blog provide better learning resources. This is an ongoing process of learning and improvement, and your suggestions are valuable to me. You can reach me at csnote.cc@gmail.com.