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 的长度。

相关推荐
吴_知遇29 分钟前
【华为OD机试真题】428、连续字母长度 | 机试真题+思路参考+代码解析(E卷)(C++)
开发语言·c++·华为od
LaoWaiHang1 小时前
MFC案例:使用键盘按键放大、缩小窗口图像的实验
c++·mfc
到底怎么取名字不会重复1 小时前
Day10——LeetCode15&560
c++·算法·leetcode·哈希算法·散列表
陈大大陈2 小时前
基于 C++ 的用户认证系统开发:从注册登录到Redis 缓存优化
java·linux·开发语言·数据结构·c++·算法·缓存
纪元A梦2 小时前
华为OD机试真题——通过软盘拷贝文件(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
数据分析螺丝钉2 小时前
LeetCode 252 会议室 III(Meeting Rooms III)题解与模拟面试
算法·leetcode·职场和发展
刚入坑的新人编程2 小时前
C++多态
开发语言·c++
QUST-Learn3D3 小时前
高精度并行2D圆弧拟合(C++)
开发语言·c++
明月醉窗台3 小时前
Qt 入门 6 之布局管理
c语言·开发语言·c++·qt
云小逸3 小时前
【C++】继承
开发语言·c++