2657. 找到两个数组的前缀公共数组 | 难度:中等

2657. 找到两个数组的前缀公共数组 | 难度:中等

题意理解(用样例说话)

题目给定两个排列 A 和 B(长度都是 n,包含 1 到 n 的每个数字恰好一次),要求我们计算「前缀公共数组」C。

关键定义 :Ci 表示在 A 和 B 的前 i+1 个元素中 (即下标 0 到 i),同时出现在两个数组前缀中的不同元素个数

用题目示例 A = [1, 3, 2, 4], B = [3, 1, 2, 4] 演示:

下标 i A 的前缀 B 的前缀 公共元素 Ci
0 1 3 0
1 1, 3 3, 1 {1, 3} 2
2 1, 3, 2 3, 1, 2 {1, 2, 3} 3
3 1, 3, 2, 4 3, 1, 2, 4 {1, 2, 3, 4} 4

输出[0, 2, 3, 4]


解法思路

核心观察

由于 A 和 B 都是排列,每个数字只会出现一次。因此,当我们遍历到下标 i 时:

  • 如果某个数字 x 既出现在 A0...i 中,又出现在 B0...i 中,那么 x 必然会被计入 Ci

直观做法:双哈希集

我们可以用两个哈希集分别记录 A 和 B 的前缀元素,然后用第三个哈希集记录它们的交集。

具体步骤

  1. 初始化三个无序集合:cntA(记录 A 的前缀元素)、cntB(记录 B 的前缀元素)、cnt(记录公共元素)
  2. 遍历下标 i 从 0 到 n-1:
    • 将 Ai 插入 cntA
    • 将 Bi 插入 cntB
    • 关键 :检查 Ai 是否已经在 cntB 中(说明 Ai 是公共元素)
    • 关键 :检查 Bi 是否已经在 cntA 中(说明 Bi 是公共元素)
    • 如果满足,将该元素加入 cnt
    • 记录 cnt.size() 到答案数组

为什么这个方法正确?

  • 由于是排列,每个数字只会出现一次,所以我们不需要计数,只需要知道「是否出现过」
  • 当我们处理下标 i 时,cntAcntB 中分别记录了 A0...i 和 B0...i 的所有元素
  • 如果 AicntB 中,说明 Ai 也出现在 B 的前缀中,因此它是公共元素
  • 同理适用于 Bi

代码实现

参考你提供的代码,我们可以这样实现(添加了详细注释):

cpp 复制代码
class Solution {
public:
    vector<int> findThePrefixCommonArray(vector<int>& A, vector<int>& B) {
        int n = A.size();
        unordered_set<int> cntA, cntB;  // 分别记录 A 和 B 的前缀元素
        unordered_set<int> cnt;           // 记录当前位置的公共元素
        vector<int> ans;

        for (int i = 0; i < n; i++) {
            // 将当前元素加入各自的前缀集合
            cntA.emplace(A[i]);
            cntB.emplace(B[i]);

            // 关键:检查 A[i] 是否也在 B 的前缀中出现过
            if (cntB.count(A[i])) {
                cnt.emplace(A[i]);  // A[i] 是公共元素
            }

            // 关键:检查 B[i] 是否也在 A 的前缀中出现过
            if (cntA.count(B[i])) {
                cnt.emplace(B[i]);  // B[i] 是公共元素
            }

            // 当前位置的公共元素个数
            ans.emplace_back(cnt.size());
        }

        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(n) ------ 只需遍历一次数组,每次哈希集操作平均 O(1)
  • 空间复杂度:O(n) ------ 三个哈希集最多各存储 n 个元素

易错点

1. 不要重复计数

由于 A 和 B 都是排列,每个数字只会出现一次 。但需要注意:当 A[i] == B[i] 时,两个 if 语句都会尝试插入同一个数字到 cnt 中。不过由于 unordered_set 会自动去重,所以最终结果是正确的。

错误示例(虽然不影响结果,但逻辑冗余):

cpp 复制代码
// 如果写成这样,当 A[i] == B[i] 时会检查两次
if (cntB.count(A[i])) cnt.emplace(A[i]);
if (cntA.count(B[i])) cnt.emplace(B[i]);  // 当 A[i]==B[i] 时,这行是多余的检查

改进写法(可选):

cpp 复制代码
if (cntB.count(A[i])) cnt.emplace(A[i]);
if (A[i] != B[i] && cntA.count(B[i])) cnt.emplace(B[i]);  // 避免重复检查

不过原代码的写法更简洁,且 unordered_set::emplace 对于重复元素会自动忽略,所以两种写法都可以。

2. 理解「前缀」的定义

Ci 计算的是到下标 i 之前(包括 i)的公共元素个数。有些同学会误解为「到下标 i 之前(不包括 i)」,从而导致 off-by-one 错误。


相关题目

  1. 349. 两个数组的交集 ------ 思路类似,都是用哈希集找公共元素
  2. 350. 两个数组的交集 II ------ 进阶版,需要考虑元素出现次数

希望这篇题解能帮助你理解这道题的核心思路!如果还有疑问,欢迎继续讨论 💬

相关推荐
地平线开发者35 分钟前
征程 6E/M Matrix 开发评板使用系列(一):开箱与点亮
算法·自动驾驶
Jerry1 小时前
LeetCode 59. 螺旋矩阵 II
算法
tachibana21 小时前
hot100 回文链表(234)
java·网络·数据结构·leetcode·链表
可编程芯片开发1 小时前
基于FOC控制器的BLDC无刷直流电机控制系统matlab编程与仿真
算法
aaaameliaaa2 小时前
进制练习题【找出只出现一次的数字、交换两个变量(不创建临时变量)、统计二进制中1的个数、打印整数二进制的奇数位和偶数位、求两个数二进制中不同位的个数】
c语言·数据结构·笔记·算法
QiLinkOS3 小时前
第三视觉理解徐玉生与他的商业活动(28)
大数据·c++·人工智能·算法·开源协议
码云数智-大飞4 小时前
从 OC 平滑迁移 Swift 完整方案
职场和发展·蓝桥杯
wabs6664 小时前
关于动态规划【力扣1143.最长公共子序列的思考】
算法·leetcode·动态规划
剑挑星河月4 小时前
54.螺旋矩阵
java·算法·leetcode·矩阵
Robot_Nav4 小时前
MPPI 局部规划器实验设计讲解
人工智能·算法·mppi