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 时,cntA 和 cntB 中分别记录了 A0...i 和 B0...i 的所有元素
  • 如果 Ai 在 cntB 中,说明 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 ------ 进阶版,需要考虑元素出现次数

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

相关推荐
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼4 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光4 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark4 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her14 天前
并查集-听课笔记
笔记·算法·并查集
码流子4 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven4 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark4 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218614 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法