444. 序列重建
Difficulty
Medium
Tags
拓扑排序
图
URL
https://leetcode.cn/problems/sequence-reconstruction/
Star
给定一个长度为
n
的整数数组 nums
,其中 nums
是范围为 [1,n]
的整数的排列。还提供了一个 2D 整数数组 sequences
,其中 sequences[i]
是 nums
的子序列。
检查 nums
是否是唯一的最短 超序列 。最短 超序列 是 长度最短 的序列,并且所有序列 sequences[i]
都是它的子序列。对于给定的数组 sequences
,可能存在多个有效的 超序列 。例如,对于
sequences = [[1,2],[1,3]]
,有两个最短的 超序列 ,[1,2,3]
和 [1,3,2]
。
而对于 sequences = [[1,2],[1,3],[1,2,3]]
,唯一可能的最短 超序列 是 [1,2,3]
。[1,2,3,4]
是可能的超序列,但不是最短的。
如果 nums
是序列的唯一最短 超序列 ,则返回 true
,否则返回 false
。
子序列 是一个可以通过从另一个序列中删除一些元素或不删除任何元素,而不改变其余元素的顺序的序列。示例 1:
输入:nums = [1,2,3], sequences = [[1,2],[1,3]] 输出:false 解释:有两种可能的超序列:[1,2,3]和[1,3,2]。 序列 [1,2] 是[1,2,3]和[1,3,2]的子序列。 序列 [1,3] 是[1,2,3]和[1,3,2]的子序列。 因为 nums 不是唯一最短的超序列,所以返回false。
示例 2:
输入:nums = [1,2,3], sequences = [[1,2]] 输出:false 解释:最短可能的超序列为 [1,2]。 序列 [1,2] 是它的子序列:[1,2]。 因为 nums 不是最短的超序列,所以返回false。
示例 3:
输入:nums = [1,2,3], sequences = [[1,2],[1,3],[2,3]] 输出:true 解释:最短可能的超序列为[1,2,3]。 序列 [1,2] 是它的一个子序列:[1,2,3]。 序列 [1,3] 是它的一个子序列:[1,2,3]。 序列 [2,3] 是它的一个子序列:[1,2,3]。 因为 nums 是唯一最短的超序列,所以返回true。
提示:
n == nums.length
1 <= n <= 104
nums
是[1, n]
范围内所有整数的排列
1 <= sequences.length <= 104
1 <= sequences[i].length <= 104
1 <= sum(sequences[i].length) <= 105
1 <= sequences[i][j] <= n
sequences
的所有数组都是 唯一 的
sequences[i]
是nums
的一个子序列
法 1 拓扑排序 + 构造
思路
为了方便,我们令 sequences 为 ss。
根据题意,如果我们能够利用所有的 ss[i] 构造出一个唯一的序列,且该序列与 nums 相同,则返回 True,否则返回 False。
将每个 ss[i] 看做对 ss[i] 所包含点的前后关系约束,我们可以将问题转换为拓扑排序问题。
利用所有 ss[i] 构造新图:对于 ss[i] = [A_1, A_2, ..., A_k],我们将其转换为点 A_1-> A_2-> ... -> A_k的有向图,同时统计每个点的入度情况。
然后在新图上跑一遍拓扑排序,构造对应的拓扑序列,与 nums 进行对比。
实现上,由于拓扑排序过程中,出点的顺序即为拓扑序,因此我们并不需要完整保存整个拓扑序,只需使用一个变量 loc 来记录当前拓扑序的下标,将出点 tt 与 nums[loc]nums[loc] 做比较即可。
在拓扑序过程中若有 tt 不等于 nums[loc]nums[loc](构造出来的方案与 nums 不同) 或某次拓展过程中发现队列元素不止 11 个(此时可能的原因有 :「起始入度为 00 的点不止一个或存在某些点根本不在 ssss 中」或「单次拓展新产生的入度为 00 的点不止一个,即拓扑序不唯一」),则直接返回 False,
题解
class Solution: def sequenceReconstruction(self, nums: List[int], sequences: List[List[int]]) -> bool: # 建 图 & 入度表 in_degree = [0 for _ in range(len(nums)+1)] in_degree[0] = float("inf") graph = collections.defaultdict(list) for seq in sequences: for i in range(1, len(seq)): graph[seq[i-1]].append(seq[i]) in_degree[seq[i]] += 1 # 找入度为0的为起点 queue = deque() for i in range(0, len(in_degree)): if in_degree[i] == 0: queue.append(i) loc = 0 while queue: if len(queue) > 1: return False cur_num = queue.popleft() if cur_num != nums[loc]: return False loc += 1 for nxt_num in graph[cur_num]: in_degree[nxt_num] -= 1 if in_degree[nxt_num] == 0: queue.append(nxt_num) return True
法2 技巧
思路
条件构成一棵树,就是在树中找是否存在一条路径是 nums。
题解
class Solution: def sequenceReconstruction(self, nums: List[int], sequences: List[List[int]]) -> bool: # 记录每个数的先后关系 tree = defaultdict(set) for seq in sequences: for i in range(1, len(seq)): tree[seq[i-1]].add(seq[i]) for i in range(1, len(nums)): if nums[i] not in tree[nums[i-1]]: return False return True