草庐IT

「双指针/kmp」通过连接另一个数组的子数组得到一个数组(力扣第1764题)

孤星·璀璨 2023-03-28 原文

本题为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核心代码 /////////
};

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

有关「双指针/kmp」通过连接另一个数组的子数组得到一个数组(力扣第1764题)的更多相关文章

  1. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  2. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  4. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  7. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  8. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐