「双指针/kmp」通过连接另一个数组的子数组得到一个数组(力扣第1764题)
本题为12月17日力扣每日一题
题目来源:力扣第1764题
题目tag:双指针
kmp
题面
题目描述
给你一个长度为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 开始)。
提示
groups.length == n
1 <= n <= $ 10^3 $
1 <= groups[i].length, sum(groups[i].length) <= $ 10^3 $
1 <= nums.length <= $ 10^3 \(
\) -10^7 $ <= groups[i][j], nums[k] <= $ 10^7 $
思路分析
这题的题目翻译过来就是,对nums数组,挨个匹配二维数组groups中的每一行,如果能全部匹配就是匹配成功,如果不能则为失败.
再简单一点说就是对nums数组进行多次子串匹配,而子串匹配显然可以直接使用kmp
算法来完成.由于本篇博客仅为题解,所以这里不对kmp
算法展开讨论,如感兴趣可以自行搜索学习,下面的参考代码也不会对kmp
算法的部分进行注释.
那如果不会kmp
算法怎么办呢?这道题的数据量比较小,所以直接使用双指针暴力比较也是可以的.kmp
其实就是对下面这样的暴力匹配的一种优化方案.
我们使用i指针指向nums,j指针指向groups,看看nums中以i指向的元素为开头的子串,是否能和当前j指向的那行groups(下用group指代)匹配:
- 如果当前匹配,那就把i移动group的长度,然后j指向下一个group,继续匹配.
- 如果不匹配,那就把i后移,匹配下一个子串.
如果最终j成功移到groups末尾,那证明groups中的所有group成功匹配,返回true,否则证明其中存在group匹配失败,那就返回false.
参考代码
双指针暴力比较
class Solution
{
public:
bool canChoose(vector<vector<int>> &groups, vector<int> &nums)
{
int i = 0, j = 0; // i为nums上的指针,j为groups上的指针
while (i < nums.size() && j < groups.size())
{
if (equal(groups[j], nums, i)) // 如果当前从i开始的子串与当前对应的group匹配
{
i += groups[j].size(); // 跳过这些已匹配部分
j++; // 匹配下一个group
}
else // 当前未匹配
{
i++; // 从下一个开始继续尝试比较
}
}
return j == groups.size(); // 如果是因为j遍历完毕而退出的循环,就是匹配成功,否者就是匹配失败
}
private:
// 比较nums中以start开头的子串是否和group匹配
bool equal(vector<int> group, vector<int> nums, int start)
{
if (group.size() > nums.size() - start) // 当前nums子串的长度已不够,必然不匹配
{
return false;
}
for (int i = 0; i < group.size(); i++) // 逐个遍历即可
{
if (group[i] != nums[start + i])
{
return false;
}
}
return true;
}
};
kmp
class Solution
{
public:
bool canChoose(vector<vector<int>> &groups, vector<int> &nums)
{
int i = 0; // i为nums上的指针
for (int k = 0; k < groups.size(); k++)
{
int j = 0; // j为当前group上的指针
///////// kmp核心代码 /////////
vector<int> next = getNext(groups[k]);
while (i < nums.size() && j < groups[k].size())
{
if (nums[i] == groups[k][j])
{
i++;
j++;
}
else if (next[j] != -1)
{
j = next[j];
}
else
{
i++;
}
}
///////// kmp核心代码 /////////
if (j != groups[k].size()) // 当前串匹配失败,则一定不匹配
{
return false;
}
}
return true;
}
private:
///////// kmp核心代码 /////////
vector<int> getNext(vector<int> group)
{
vector<int> next(group.size(), -1);
if (group.size() == 1)
{
return next;
}
next[1] = 0;
int i = 2, cn = 0;
while (i < group.size())
{
if (group[i - 1] == group[cn])
{
next[i++] = ++cn;
}
else if (cn > 0)
{
cn = next[cn];
}
else
{
next[i++] = 0;
}
}
return next;
}
///////// kmp核心代码 /////////
};
"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德
这里是浙江理工大学22届ACM集训队的成员一枚鸭!
本文首发于博客园,作者:星双子,除了我自己的转载请注明原文链接:https://www.cnblogs.com/geministar/p/LeetCode1764.html