通过连接另一个数组的子数组得到一个数组
给你一个长度为 n 的二维整数数组 groups ,同时给你一个整数数组 nums 。
你是否可以从 nums 中选出 n 个 不相交 的子数组,使得第 i 个子数组与 groups[i] (下标从 0 开始)完全相同,且如果 i > 0 ,那么第 (i-1) 个子数组在 nums 中出现的位置在第 i 个子数组前面。(也就是说,这些子数组在 nums 中出现的顺序需要与 groups 顺序相同)
如果你可以找出这样的 n 个子数组,请你返回 true ,否则返回 false 。
如果不存在下标为 k 的元素 nums[k] 属于不止一个子数组,就称这些子数组是 不相交 的。子数组指的是原数组中连续元素组成的一个序列。
示例 1:
输入:groups = [[1,-1,-1],[3,-2,0]], nums = [1,-1,0,1,-1,-1,3,-2,0]
输出:true
解释:你可以分别在 nums 中选出第 0 个子数组 [1,-1,0,1,-1,-1,3,-2,0] 和第 1 个子数组 [1,-1,0,1,-1,-1,3,-2,0] 。
这两个子数组是不相交的,因为它们没有任何共同的元素。
示例 2:
输入:groups = [[10,-2],[1,2,3,4]], nums = [1,2,3,4,10,-2]
输出:false
解释:选择子数组 [1,2,3,4,10,-2] 和 [1,2,3,4,10,-2] 是不正确的,因为它们出现的顺序与 groups 中顺序不同。
[10,-2] 必须出现在 [1,2,3,4] 之前。
示例 3:
输入:groups = [[1,2,3],[3,4]], nums = [7,7,1,2,3,4,7,7]
输出:false
解释:选择子数组 [7,7,1,2,3,4,7,7] 和 [7,7,1,2,3,4,7,7] 是不正确的,因为它们不是不相交子数组。
它们有一个共同的元素 nums[4] (下标从 0 开始)。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/form-array-by-concatenating-subarrays-of-another-array
方法一:贪心+双指针
class Solution:
def canChoose(self, groups: List[List[int]], nums: List[int]) -> bool:
n=0
ans=[False]*len(groups)
for i in range(len(groups)):
for j in range(n,len(nums)):
if groups[i]!=nums[j:j+len(groups[i])]:
n=n+1
if groups[i]==nums[j:j+len(groups[i])]:
n=n+len(groups[i])
ans[i]=True
break
return all(ans)
解析
先定义指针n,表示数组nums下一次与groups[i]比较的位置;
定义元素全为False的数组ans,长度与groups的长度相同,ans[i]用来反映groups[i]是否满足题目条件;
套用两层for循环,对groups的子数组与nums进行匹配,匹配不成功,n+1,确保下一个子数组匹配时,头元素匹配的位置不会发生重复,造成失误;匹配成功时,n+该子数组的长度,下次匹配不会与已匹配元素产生交集,并且置ans[i]为True,退出当前循环,进行下一子数组的匹配;
全部匹配完成后,返回all(ans),如果ans元素全为true,all(ans)为true;否则为False;
复杂度
时间复杂度:O(n*m)
空间复杂度:O(1)
方法二:kmp算法
class Solution:
def canChoose(self, groups: List[List[int]], nums: List[int]) -> bool:
def Get_next(p,next):
nums=len(p)
for m in range(1,nums):
k=0
for i in range(0,m):
if p[0:i]==p[m-i:m]:
k=max(k,i)
next.append(k)
nums1=len(nums)
i=0
ans=[]
for p in groups:
nums2=len(p)
next=[0]
Get_next(p,next)
j=0
while i!=nums1 and j!=nums2:
if nums[i]!=p[j]:
if j==0:
i+=1
j=next[j]
else:
i+=1
j+=1
if j!=nums2:
ans.append(False)
else:
ans.append(True)
return all(ans)
解析
用kmp算法,首先写一个求模式串next[j]的函数,意思是模式串中当前指针所表示元素与主串的指针所表示元素不匹配时,模式串的指针应该回溯到的位置;
因为模式串的第一个元素不匹配时,不回溯;所以我们直接从第二个元素开始,用两层for循环遍历求每个元素的next[j],重点是 if p[0:i]==p[m-i:m]:k=max(k,i);m是未匹配成功时的指针位置,以模式串的首个元素开始的一个子序列p1,以m指示的上一个元素为末尾的一个子序列p2,p1=p2时,p1的末尾元素索引i-1最大时,取i作为指针回溯的位置;
定义一个数组ans用来记录groups的子数组的匹配情况;遍历groups的每个子数组,先用上面的函数求next[j],如果指针i,j都满足数组的长度限制,就对指针所指示元素进行匹配,如果相同,i,j指针同时后移;如果不同,j指针回溯到符合kmp算法的next[j]位置,与i指针进行匹配,直到j指针回溯到首元素位置时,i指针才向后移动;如果主串匹配完了,或模式串匹配完了,退出while循环;如果模式串匹配完,即j=nums2,则该子数组符合题目要求;反之,不符合;最后看ans数组是否都符合要求,解决需求。
复杂度
时间复杂度:O(m+∑ni),m为nums的长度;
空间复杂度:O(n),n为groups的长度