代码随想录Day5-数组-长度最小的子数组&螺旋矩阵:力扣76、59、54题

76. 最小覆盖子串

题目链接

方法一:滑动窗口

思路

哈希表tFreq记录字符串t各个字符出现的频数,维护一个滑动窗口,若窗口内的字符出现的频数都大于等于需要的频数,则窗口对应的子串是符合要求的。

窗口移动逻辑

移动右边界寻找符合要求的子串,当找到符合要求的子串后,停止移动右边界,因为此时继续移动右边界得到的子串肯定也是符合要求的,但是因为长度会增加,所以肯定不是最小子串;

此时开始移动左边界,因为移动左边界后得到的子串也可能满足要求,且长度更短;

移动左边界直到当前窗口不满足要求,此时停止移动左边界,因为当前都不符合要求,继续缩短肯定也不符合要求;此时改为移动右边界。

判断窗口内子串是否符合要求

用一个哈希表winFreq记录当前子串内各字符的频数,用一个数count来表示当前符合条件的字符数量,当count等于tFreq时,说明所有字符都满足要求,此时当前子串就是符合要求的。

注意修改count的条件,只有当winFreq[s[right]] == tFreq[s[right]]时才修改。

  • 时间复杂度: O ( C n + m ) O(Cn+m) O(Cn+m),C为t的字符集的数量,n为s的长度,m为t的长度。因为遍历s的时候,每一步都需要遍历一次字符集查看当前字符是否在字符集中,所以有一项 C n Cn Cn
  • 空间复杂度: O ( C ) O(C) O(C)。需要哈希表存储字符集的频数。
C++代码
class Solution {
public:
    string minWindow(string s, string t) {
        int left = 0;
        int right = 0;
        int count = 0;
        int size = s.size();
        int start = 0;
        int len = INT_MAX;
        unordered_map<char, int> tFreq;  // t中各个字母出现的频数
        unordered_map<char, int> winFreq;  // 滑动窗口中各个字母出现的频数

        // 统计t中各个字母出现的频数
        for (char& c : t) ++tFreq[c];
        int tFreqSize = tFreq.size();

        while (right < size) {
            // 移动右边界寻找可行解
            if (tFreq.count(s[right]) != 0) {
                ++winFreq[s[right]];
                if (winFreq[s[right]] == tFreq[s[right]]) {
                    ++count;
                }
            }
            // 只要count==tFreqSize说明当前滑动窗口内的子串是符合条件的
            // 移动左边界寻找最优解,直到窗口内子串不符合条件再继续移动右边界寻找可行解
            while (count == tFreqSize) {
                if (len > right - left + 1) {
                    start = left;
                    len = right - left + 1;
                }
                if (tFreq.count(s[left]) != 0) {
                    if (tFreq[s[left]] == winFreq[s[left]]) {
                        --count;
                    }
                    --winFreq[s[left]];
                }
                ++left;
            }
            ++right;
        }
        return len == INT_MAX ? "" : s.substr(start, len);
    }
};

看完讲解的思考

左右窗口不要同时移动,一次只移动一边,搞清楚移动左右边界的条件以及目的。
一开始总想着移动边界时一步到位,想着移动左边界要直接移动到下个满足条件的子串,搞得代码逻辑十分混乱,但其实每次只需移动一步就够了。

滑动窗口题目的要点:

  • 左右边界不要同时移动。
  • 边界一次只移动一步。
  • 移动左右边界的条件逻辑。
  • 移动右边界是为了找可行解,移动左边界是为了找最优解

代码实现遇到的问题

  • 没有理清楚边界移动的逻辑
  • 判断窗口内子串是否符合条件时处理得过于复杂

59. 螺旋矩阵II

题目链接
代码随想录文章讲解链接

方法一:模拟1

思路

模拟顺时针螺旋填入数字的过程,设置变量state作为当前的方向,1、2、3、4分别表示方向上下左右,每次走的时候判断是否已经到头(边界或者已填的部分),没到的话就继续沿着当前方向填,到头的话就改变方向。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, -1));
        int state = 4;  // 1-上 2-下 3-左 4-右
        int x = 0;
        int y = 0;

        for (int i = 1; i <= n * n; ++i) {
            res[x][y] = i;
            if (state == 1) {
                if (res[x - 1][y] == -1) x -= 1;
                else {
                    y += 1;
                    state = 4;
                }
            } else if (state == 2) {
                if (x + 1 < n && res[x + 1][y] == -1) x += 1;
                else {
                    y -= 1;
                    state = 3;
                }
            } else if (state == 3) {
                if (y - 1 >= 0 && res[x][y - 1] == -1) y -= 1;
                else {
                    x -= 1;
                    state = 1;
                }
            } else if (state == 4) {
                if (y + 1 < n && res[x][y + 1] == -1) y += 1;
                else {
                    x += 1;
                    state = 2;
                }
            }
        }
        return res;
    }
};

方法二:模拟2

思路

直接用4个for循环以此填入矩阵每圈的四条边。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int left = 0;
        int right = n - 1;
        int top = 0;
        int down = n - 1;
        int count = 1;
        vector<vector<int>> res(n, vector<int>(n, -1));

        while (count <= n * n) {
            for (int i = left; i <= right; ++i) res[top][i] = count++;
            ++top;
            for (int i = top; i <= down; ++i) res[i][right] = count++;
            --right;
            for (int i = right; i >= left; --i) res[down][i] = count++;
            --down;
            for (int i = down; i >= top; --i) res[i][left] = count++;
            ++left;
        }
        return res;
    }
};

看完讲解的思考

代码实现遇到的问题


54. 螺旋矩阵

题目链接

方法一:模拟

思路

做法与上一题大同小异,不同的地方在于结束循环的条件。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int row = matrix.size();
        int column = matrix[0].size();
        int left = 0;
        int right = column - 1;
        int top = 0;
        int down = row - 1;
        vector<int> res;

        while (res.size() != row * column) {
            for (int i = left; i <= right; ++i) res.push_back(matrix[top][i]);
            ++top;
            for (int i = top; i <= down; ++i) res.push_back(matrix[i][right]);
            --right;
            if (res.size() == row * column) break;
            else {
                for (int i = right; i >= left; --i) res.push_back(matrix[down][i]);
                --down;
                for (int i = down; i >= top; --i) res.push_back(matrix[i][left]);
                ++left;
            }
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

一开始没有考虑清楚循环结束的条件。


最后的碎碎念

今日第一题是hard,折腾了一个多钟,第一次做hard,有点顶的…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值