题目描述

✅ 1640. 能否连接形成数组

image-20260929090014840

image-20260929090014938

题意分析

将 pieces 中的完整数组块重新排列,连接后得到 arr,每块内部的元素顺序必须保持。题目保证两边总长度相同,arr 中元素互不相同,所有块合在一起的元素也互不相同。

解法:映射首元素匹配

核心思路

[!blue]
用 i 指向 arr 的第一个未匹配位置,已经经过的前缀都由完整块组成。下一块不能拆开,也不能调整内部顺序,因此它的首元素必须是 arr[i]。所有块的元素互不相同,这样的块至多只有一块,可以预先建立“首元素到整块”的映射直接查找。

找不到对应块就无解。找到后,必须逐项与 arr 的当前位置比较;越过数组末尾或任一元素不同,也都无解,因为没有另一块可以替代这个被首元素唯一确定的选择。整块通过后,让 i 前进块长,继续保持前缀已经完整拼好的状态。

无需额外记录块是否用过:若再次使用同一块,它的首元素就会在 arr 中重复,违反元素唯一的前提。最终完整覆盖 arr 时,已用块总长度等于 arr 长度,而所有块的总长度也相同,因此没有剩余未用块。两边元素集合是否一致由实际匹配验证,不需要提前假定。

解题步骤

  1. 按每块首元素建立映射。
  2. 从 arr 的首个未匹配位置查找对应块。
  3. 逐项核对该块,缺失或不匹配就返回 false。
  4. 整块通过后前进对应长度,全部覆盖则返回 true。

代码实现

class Solution {
    public boolean canFormArray(int[] arr, int[][] pieces) {
        // 元素互不相同,首元素可以唯一标识一块 piece。
        Map<Integer, int[]> map = new HashMap<>();

        for (int[] p : pieces) {
            map.put(p[0], p);
        }

        int i = 0;

        while (i < arr.length) {
            int[] p = map.get(arr[i]);

            if (p == null) {
                return false;
            }

            for (int j = 0; j < p.length; j++) {
                // 先判越界再比值,防止 piece 长过 arr 的剩余部分。
                if (i + j >= arr.length || arr[i + j] != p[j]) {
                    return false;
                }
            }

            i += p.length;
        }

        return true;
    }
}
func canFormArray(arr []int, pieces [][]int) bool {
    // 元素互不相同,首元素可以唯一标识一块 piece。
    m := make(map[int][]int)
    for _, p := range pieces {
        m[p[0]] = p
    }

    i := 0
    for i < len(arr) {
        p, ok := m[arr[i]]
        if !ok {
            return false
        }
        for j := 0; j < len(p); j++ {
            // 先判越界再比值,防止 piece 长过 arr 的剩余部分。
            if i+j >= len(arr) || arr[i+j] != p[j] {
                return false
            }
        }
        i += len(p)
    }
    return true
}

复杂度分析

  • 时间复杂度:期望 $O(n+k)$,n 为 arr 长度,k 为块数。
  • 空间复杂度:$O(k)$,映射只保存原块引用。

关键点总结

[!green]

  • 首元素唯一,下一块无需枚举。
  • 必须逐项检查块内部顺序。
  • 完整块覆盖与总长度相同使额外已用集合不必要。

易错点总结

[!yellow]

  • 只检查首元素就跳过整块:可能接受内部顺序错误的块。
  • 通过一块后只前进一位:会把块内部元素误当成下一块起点。
  • 按 pieces 输入顺序直接拼接:题目允许重排块。
  • 排序两边后只比较元素集合:丢失块内不可变的顺序约束。

相似题目

题目 难度 关联与区别
139. 单词拆分 中等 原题词块前缀可能重叠而需要DP,本题数值唯一,可由当前首值定位唯一候选块再逐项核对。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2023/17889675
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!