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

相关推荐
十年一梦实验室6 分钟前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong00110 分钟前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我5817 分钟前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc24 分钟前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
呆萌很42 分钟前
C++ 集合 list 使用
c++
诚丞成2 小时前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
东风吹柳2 小时前
观察者模式(sigslot in C++)
c++·观察者模式·信号槽·sigslot
A懿轩A3 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
大胆飞猪4 小时前
C++9--前置++和后置++重载,const,日期类的实现(对前几篇知识点的应用)
c++
1 9 J4 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法