Leetcode3036. 匹配模式数组的子数组数目 II

Every day a Leetcode

题目来源:3036. 匹配模式数组的子数组数目 II

解法1:KMP

设数组 nums 的长度为 m,数组 pattern 的长度为 n。

遍历数组 nums 的每个长度是 n+1 的子数组并计算子数组的模式,然后与数组 pattern 比较,如果相等则找到一个匹配模式数组的子数组。遍历结束之后即可得到匹配模式数组的子数组数目。

我们发现这其实就是 KMP。

将匹配模式转换成字符串:1 对应 'a',0 对应 'b',-1 对应 'c'。

代码:

c 复制代码
/*
 * @lc app=leetcode.cn id=3036 lang=cpp
 *
 * [3036] 匹配模式数组的子数组数目 II
 */

// @lc code=start

// KMP

class Solution
{
private:
    // KMP 算法
    vector<int> getNxt(string &pattern)
    {
        vector<int> nxt;
        // next[0] 必然是 0
        nxt.push_back(0);
        // 从 next[1] 开始求
        int x = 1, now = 0;
        while (x < pattern.length())
        {
            if (pattern[now] == pattern[x])
            {
                // 如果 pattern[now] == pattern[x],向右拓展一位
                now++;
                x++;
                nxt.push_back(now);
            }
            else if (now != 0)
            {
                // 缩小 now,改成 nxt[now - 1]
                now = nxt[now - 1];
            }
            else
            {
                // now 已经为 0,无法再缩小,故 next[x] = 0
                nxt.push_back(0);
                x++;
            }
        }
        return nxt;
    }

    vector<int> kmp(string &s, string &pattern)
    {
        int m = pattern.length();
        vector<int> nxt = getNxt(pattern);
        vector<int> res;
        int tar = 0; // 主串中将要匹配的位置
        int pos = 0; // 模式串中将要匹配的位置
        while (tar < s.length())
        {
            if (s[tar] == pattern[pos])
            {
                // 若两个字符相等,则 tar、pos 各进一步
                tar++;
                pos++;
            }
            else if (pos != 0)
            {
                // 失配,如果 pos != 0,则依据 nxt 移动标尺
                pos = nxt[pos - 1];
            }
            else
            {
                // pos[0] 失配,标尺右移一位
                tar++;
            }

            if (pos == pattern.length())
            {
                res.push_back(tar - pos);
                pos = nxt[pos - 1];
            }
        }
        return res;
    }

public:
    int countMatchingSubarrays(vector<int> &nums, vector<int> &pattern)
    {
        // 特判
        if (nums.empty() || pattern.empty())
            return 0;
        if (nums.size() <= pattern.size())
            return 0;

        int count = 0;
        int m = nums.size(), n = pattern.size();

        // 1 对应 'a',0 对应 'b',-1 对应 'c'
        string s;
        for (int i = 0; i < m - 1; i++)
        {
            int diff = nums[i + 1] - nums[i];
            int p = getPattern(diff);
            if (p == 1)
                s += "a";
            else if (p == 0)
                s += "b";
            else
                s += "c";
        }

        string p;
        for (int &pa : pattern)
        {
            if (pa == 1)
                p += "a";
            else if (pa == 0)
                p += "b";
            else
                p += "c";
        }

        return kmp(s, p).size();
    }
    // 辅函数 - 计算 pattern
    int getPattern(int diff)
    {
        if (diff == 0)
            return 0;
        return diff > 0 ? 1 : -1;
    }
};
// @lc code=end

结果:

复杂度分析:

时间复杂度:O(m),其中 m 是数组 nums 的长度。

空间复杂度:O(n),其中 n 是数组 pattern 的长度。

解法2:Z 函数(扩展 KMP)

代码:

c 复制代码
// Z 函数(扩展 KMP)

class Solution
{
public:
    int countMatchingSubarrays(vector<int> &nums, vector<int> &pattern)
    {
        int m = pattern.size();

        // 为了防止匹配越界,中间插入一个不在数组中的数字
        pattern.push_back(2);

        for (int i = 1; i < nums.size(); i++)
        {
            int x = nums[i - 1], y = nums[i];
            // if (x < y)
            //     pattern.push_back(1);
            // else if (x == y)
            //     pattern.push_back(0);
            // else
            //     pattern.push_back(-1);
            pattern.push_back((y > x) - (y < x));
        }

        int n = pattern.size();
        vector<int> z(n);

        int l = 0, r = 0; // Z box 的左右边界
        for (int i = 1; i < n; i++)
        {
            if (i <= r) // i 在 Z box 内
            {
                z[i] = min(z[i - l], r - i + 1);
            }
            // 继续向后暴力匹配
            while (i + z[i] < n && pattern[z[i]] == pattern[i + z[i]])
            {
                l = i;
                r = i + z[i];
                z[i]++;
            }
        }

        int ans = 0;
        for (int i = m + 1; i < n; i++)
        {
            if (z[i] >= m)
                ans++;
        }

        return ans;
    }
};

结果:

复杂度分析:

时间复杂度:O(n),其中 n 是数组 nums 的长度。

空间复杂度:O(n),其中 n 是数组 nums 的长度。

相关推荐
Hua-Jay2 分钟前
OpenCV联合C++/Qt 学习笔记(十六)----图像细化、轮廓检测、轮廓信息统计及轮廓外接多边形
c++·笔记·qt·opencv·学习·计算机视觉
Tisfy3 分钟前
LeetCode 1914.循环轮转矩阵:大模拟(数组原地轮转) —— 附O(1)空间版本
算法·leetcode·矩阵·大模拟
罗超驿8 分钟前
3.快乐数专题学习笔记——双指针法在LeetCode 202题中的应用
java·算法·leetcode·职场和发展
无限进步_10 分钟前
【C++】深入底层:自己动手实现一个哈希表
开发语言·数据结构·c++·算法·链表·散列表·visual studio
_深海凉_11 分钟前
LeetCode热题100-小于 n 的最大数(字节高频题)
算法·leetcode·职场和发展
小雅痞16 分钟前
[Java][Leetcode middle] 36. 有效的数独
java·算法·leetcode
paeamecium20 分钟前
【PAT甲级真题】- General Palindromic Number(20)
数据结构·c++·算法·pat考试·pat
minji...21 分钟前
Linux 网络基础之UDP协议(四)传输层协议 UDP,再谈端口号,UDP 特点
linux·服务器·开发语言·网络·c++·tcp/ip·udp
北顾笙98021 分钟前
day43-数据结构力扣
数据结构·算法·leetcode
艾莉丝努力练剑27 分钟前
【Linux网络】Linux 网络编程:应用层自定义协议与序列化(1)初识
linux·运维·服务器·网络·c++·udp·tcp