一、题目描述
二、题目分析
这道题的暴力解法就是:枚举 nums1 中的每个元素,找到该元素在 nums2 中的位置,然后在 nums1 和 nums2 的剩余部分中寻找一个好二元组。不难发现,这样算法的复杂度为 O(n^3),不能满足我们的要求。上述算法的问题在于每次搜索都无法利用前面搜索过程得到的任何信息。
关键点1:nums2 中的元素如果被访问过,在后续访问时是不用处理的,因为这样的元素对在 nums1 和 nums2 中必然是逆序的
如果使用这个信息,那么我们就可以在遍历过程中构造一个数组,用于存储nums2 中每个位置其后面已经被访问过的元素个数。然而,即使知道这个数据,我们也无法判断出后面那些没被访问的数据能够构成多少个好二元组。因此,需再次遍历 nums1,这样的算法复杂度仍为 O(n^3)。这是因为我们仍旧没有充分利用前面得到的信息。
关键点2:总的已经被访问过的元素个数就是当前所遍历的 nums1 元素的下标,那么我们可以得到 nums2 对应元素下标其前面、后面被访问过的、未被访问过的元素个数
这让我们很自然就可以想到,如果遍历 nums1 时,把每个元素当成好三元组的中间元素,那么问题就变成了计算对应 nums2 中元素所在位置前面出现过的元素个数和后面未出现的元素个数。
三、实现
1、初步实现
在初次实现中我们使用 vector 保存所有访问过的元素在 num2 中的下标。在遍历过程中,使用二分查找(lower_bound)确定在某个下标前已访问过的元素个数,并插入该下标:
long long goodTriplets(vector<int>& nums1, vector<int>& nums2) {
// 1.将nums2中的每个数及其对应下标放入map中保存
unordered_map<int, int> num2Pos;
for (int i = 0; i < nums2.size(); ++i) {
num2Pos[nums2[i]] = i;
}
// 2.初始化数组用于按序保存所有访问过的元素
vector<int> visited;
visited.push_back(num2Pos[nums1[0]]);
// 3.遍历nums1
long long tripletsCount = 0;
for (int i = 1; i < nums1.size(); ++i) {
// 2.1 确定nums1中的元素在nums2的下标
int index = num2Pos[nums1[i]];
// 2.2 计算后面未访问过的和前面已访问过的,并插入该当前下标到visited
int behindUnvisited = 0;
int beforeVisited = 0;
if (visited.back() >= index) {
auto iterVisited = lower_bound(visited.begin()