wy的leetcode刷题记录_Day23

wy的leetcode刷题记录_Day23

目录

  • wy的leetcode刷题记录_Day23
    • 862. 和至少为 K 的最短子数组
      • 题目介绍
      • 思路
      • 代码
      • 收获
    • 392. 判断子序列
      • 题目介绍
      • 思路
      • 代码
      • 收获

862. 和至少为 K 的最短子数组

今天的每日一题是:862. 和至少为 K 的最短子数组

题目介绍

  1. 给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1。
  2. 子数组是数组中连续的一部分。

示例 1:
输入:nums = [1], k = 1
输出:1

示例 2:
输入:nums = [1,2], k = 4
输出:-1

思路

首先读完题目,我们知道题目是要求最短满足要求的子数组,而且对子数组的要求是要连续的,首先我们考虑暴力法来做这一道题,也就是用一个俩次嵌套循环遍历俩个指针,一个指向子数组的起点,一个指向子数组的终点,然后使俩个指针中间位置的所有数相加看是否大于等于k如果满足的话记录长度,用一个变量来维护最小长度。但是这样做会超时,因为你的嵌套循环每一次迭代都得计算子数组的和这样很耗时。根据以前的经验,我们通常知道当我们需要大量计算一个范围之和的时候我们考虑采用前缀和的思路,我们令求出来的前缀和数组为cumPre,他的意义就是cumPre[j]-cumPre[i]代表的是原数组[i,j]之和(j>=i),这样我们的计算量就少了一个量级,再继续采用上述想法维护最小长度即可。最后在第一次优化的基础上再优化,也就是觉得这个嵌套循环的次数其实可以砍掉一部分的,这里我们称俩个指针一个是start一个是end分别代表子数组的开始和结束,(代码里我们并没有用双指针而是直接将这俩个指针范围内的下标存贮到队列里,通过双端队列进行维护),我们发现当循环到满足条件的数组时,我们要记录最小值,并且要将start指针向后移,这样再end后移时才有可能找出更小长度的满足要求的子数组。然后再删除 q 中所有大于等于curSum 的元素,这样每次加进去的元素都会是 q 中的唯一最大值,使得 q 中的元素是按照添加顺序严格单调递增的,我们知道单调队列是满足这样的性质的。

代码

class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        int n=nums.size();
        vector<long> preSumArr(n+1);

        for(int i=0;i<n;i++)
        {
            preSumArr[i+1]=preSumArr[i]+nums[i];
        } 
        int res=n+1;
        deque<int> qu;
        for(int i=0;i<=n;i++)
        {
            long curSum=preSumArr[i];
            while(!qu.empty()&&curSum-preSumArr[qu.front()]>=k)
            {
                res=min(res,i-qu.front());
                qu.pop_front();

            }
            while(!qu.empty()&&preSumArr[qu.back()]>=curSum)
            {
                qu.pop_back();
            }
            qu.push_back(i);
        }
        return res<n+1?res:-1;
    }
};

收获

熟练了双向队列的知识,最重要的是巩固了前缀和的应用,昨天也有一题前缀和和后缀和的应用,加固了对其的认知。

392. 判断子序列

392. 判断子序列

题目介绍

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

示例 1:
输入:s = “abc”, t = “ahbgdc”
输出:true

示例 2:
输入:s = “axc”, t = “ahbgdc”
输出:false

思路

  1. 思路一:简单模拟,双指针贪心匹配,就是用俩个指针分别指向俩个字符串开始遍历,如果相同则同时后移,如果不相等则t后移,最后比较相等的字符个数是否跟s的长度相等就欧克。
  2. 思路二(可处理进阶问题中大数据量操作):
    根据题目意思一看多个数据操作就知道这一定是一道动态规划的问题,当我们把子数组的答案存贮再数组中的时候我们再访问所需的时间复杂度就是O(1)了。
    1. 确定dp数组(dp table)以及下标的含义
      dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同⼦序列的⻓度为dp[i][j]。
    2. 确定dp数组的递推公式:
      • if (s[i - 1] == t[j - 1])说明:t中找到了⼀个字符在s中也出现了,那么dp[i][j] = dp[i - 1][j - 1] + 1
      • if (s[i - 1] !=t[j - 1])说明:相当于t要删除元素,继续匹配,那么dp[i][j] = dp[i][j - 1]
    3. 初始化:这⾥dp[i][0]和dp[0][j]是没有含义的,仅仅是为了给递推公式做前期铺垫,所以初始化为0。vector> dp(s.size() + 1, vector(t.size() + 1, 0));

代码

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n=s.size();
        int m=t.size();
        if(n>m)
            return false;
        int count=0;
        for(int i=0;i<m;i++)
        {
            if(s[count]==t[i])
            {
                count++;
            }
        }

        return count==n;
    }
};
class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n=s.size();
        int m=t.size();
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                // if(s[i-1]==t[j-1])
                // {
                //     dp[i][j]=dp[i-1][j-1]+1;
                // }
                // else
                // {
                //     dp[i][j]=dp[i][j-1];
                // }
                dp[i][j]=s[i-1]==t[j-1]?dp[i-1][j-1]+1:dp[i][j-1];
            }
        }
        if(dp[n][m]==n)
            return true;
        return false;
    }
};

收获

你可能感兴趣的:(Leetcode刷题记录,leetcode,算法,数据结构)