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 ------ 进阶版,需要考虑元素出现次数

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

相关推荐
noipp3 小时前
推荐题目:洛谷 P10907 [蓝桥杯 2024 国 B] 蚂蚁开会
c语言·c++·算法·编程·洛谷
程序员二叉4 小时前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc
程序员二叉4 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
青山木4 小时前
Hot 100 --- 轮转数组
java·数据结构·算法
徐小夕5 小时前
Loop Engineering 深度解析与实战指南(全网最全)
前端·算法·github
北域码匠6 小时前
SHA-1算法:安全哈希原理与应用解析
算法·c#·哈希算法
手写码匠6 小时前
手写 GraphRAG:从零实现图增强检索增强生成系统
人工智能·深度学习·算法·aigc
BomanGe16 小时前
NSK重载高刚性滚珠丝杠技术详解
经验分享·算法·规格说明书
Matrix_117 小时前
手机里的计算摄影:广角形变校正算法
人工智能·算法·智能手机·计算摄影
WBluuue7 小时前
数据结构与算法:有序表(二):跳表
数据结构·c++·算法·skiplist