当前位置: 首页 > news >正文

LeetCode热题100JS(17/100)第三天|76.最小覆盖子串

 76. 最小覆盖子串

题目链接:76. 最小覆盖子串

难度:困难

刷题状态:2刷

新知识:

- `'A'.codePointAt(0)` 读A的ACSII码值

- `tmp.join(',')`  把数组压缩成一个字符

- `map={}    Object.keys(map).length`  map的key的个数

解题过程

思考

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

2刷了,滑动窗口问题,要返回最小子串,注意 s 和 t 由英文字母组成,我们可以先生成存储空间128(ASCII的长度)

手搓出来了,但是速度很慢很慢

var minWindow = function(s, t) {
    if(s.length<t.length) return ''
    let n=s.length
    let left=0,right=0
    let cnt=Array(128).fill(0)
    let set=new Set(t)
    let tmp=[],res=''
    for(let i of t){
        cnt[i.codePointAt(0)]+=1
    }
    while(right<n){
        if(set.has(s[right])){
            cnt[s[right].codePointAt(0)]-=1
        }
        tmp.push(s[right])
        if(isOk(set,cnt)&&set.has(s[right])){
            res=res==''?s:res
            //开始收left
            while(left<=right){
                if(set.has(s[left])&&cnt[s[left].codePointAt(0)]==0){
                    if(tmp.length<=res.length) res=tmp.join('')
                    break
                }else{
                    if(set.has(s[left])){
                        cnt[s[left].codePointAt(0)]+=1
                    }
                    tmp.shift()
                }
                left++
            }
        }
        right++
    }
    return res
}
function isOk(set,cnt){
    for(let i of set){
        if(cnt[i.codePointAt(0)]>0) return false
    }
    return true
}

这种方法肯定不推荐的。

题解分析

参考题解链接:最小覆盖子串

那么去掉存tmp可以吗

是否是最小长度可以用right-left来判断,不用tmp.length

修改如下

var minWindow = function(s, t) {
    if(s.length<t.length) return ''
    let n=s.length
    let left=0,right=0
    let map={}
    for(let i of t){
        if(!map[i]) map[i]=0
        map[i]++
    }
    //这个map是我们的目标
    //num 指的是字符串 t 中不同字符的种类数量。这是为了确定何时滑动窗口包含了 t 中所有不同的字符。
    let num=Object.keys(map).length
    //formed ++说明window中已经有一个字符的数量是符合的了,
    //当formed==num就说明窗口内符合条件可以进行判断了
    let formed=0
    let [minLen,minWindow]=[Infinity,'']//初始化
    //用于跟踪当前滑动窗口中每个字符出现次数的对象
    let window={}

    while(right<n){
        let r=s[right]
        if(map[r]){
            if(!window[r]) window[r]=0
            window[r]++
            if(window[r]==map[r]) formed++
        }
        //这里要先加上,后面计算长度用得着
        right++
        while(left<right&&formed==num){
            //开始移动left
            let l=s[left]
            if(minLen>right-left){
                minLen=right-left
                minWindow=s.slice(left,right)
            }
            if(map[l]){
                window[l]--
                if(window[l]<map[l]) formed--
            }
            left++
        }
    }
    return minWindow
}

手搓答案(无非废话版)

var minWindow = function(s, t) {
    let n=s.length
    let res=[],map={},window={}
    let left=0,right=0
    let [minLen,minWindow]=[Infinity,'']
    for(let i of t){
        if(!map[i]) map[i]=0
        map[i]++
    }
    let num=Object.keys(map).length,formed=0
    while(right<n){
        let r=s[right]
        if(map[r]){
            if(!window[r]) window[r]=0
            window[r]++ 
            if(map[r]==window[r]) formed++
        }
        right++
        while(left<right&&formed==num){
            let l=s[left]
            if(minLen>right-left){
                minLen=right-left
                minWindow=s.slice(left,right)
            }
            if(map[l]){
                window[l]--
                if(window[l]<map[l]) formed--
            }
            left++
        }
    }
    return minWindow
}

总结

 这道题还是有难度的,,,多练多记把

 53. 最大子数组和

题目链接:53. 最大子数组和

难度:中等

刷题状态:1刷

新知识:

- `let numss=nums.slice()`  这种拷贝方式不会影响原数组

解题过程

思考

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

这道题手搓了半天还是没解出来,可能是想偏了,直接看答案

题解分析

参考题解链接:最大子序和

/**
 * @param {numder[]} nums
 * @return {number}
 */

var maxSubArray = function(nums) {
    //当前考虑的子数组的最大和
    //到目前为止找到的最大子数组和
    let current=nums[0]
    let max=nums[0]
    for(let i=1;i<nums.length;i++){
        current=Math.max(nums[i],current+nums[i])
        max=Math.max(max,current)
    }
    return max
};

手搓答案(无非废话版)

总结

 不会写就直接看答案记套路,不要浪费时间,,,,

 56. 合并区间

题目链接:56. 合并区间

难度:中等

刷题状态:1刷

新知识:

解题过程

思考

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

类型是普通数组,

这题还是比较简单的

题解分析

参考题解链接:合并区间

手搓答案(无非废话版)

/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */
var merge = function(intervals) {
    intervals.sort((a,b)=>a[0]-b[0])
    let n=intervals.length
    let cur=intervals[0]
    let res=[]
    for(let i=1;i<n;i++){
        if(cur[1]<intervals[i][0]){
            res.push(cur)
            cur=intervals[i]
            continue
        }
        cur[1]=Math.max(cur[1],intervals[i][1])
    }
    res.push(cur)
    return res
};

总结

 直接用cur表示[start,end],以及排序之后不用更新cur[0]

 189. 轮转数组

题目链接:189. 轮转数组

难度:中等

刷题状态:1刷

新知识:

解题过程

思考

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

还是普通数组分类

手搓出来了

题解分析

参考题解链接:旋转数组

手搓答案(无非废话版)

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {void}
  */
var rotate = function(nums, k) {
    let tmp=nums.slice(),n=nums.length
    if(n==1) return
    if(k>n) k=k%n
    for(let i =0;i<n;i++){
        if(n-k+i<=n-1){
            nums[i]=tmp[n-k+i]
        }else{
            nums[i]=tmp[i-k]
        }
    }
};

总结

 注意处理一下k>n的情况就行了

 238. 除自身以外数组的乘积

题目链接:​​​​​​​238. 除自身以外数组的乘积

难度:中等

刷题状态:1刷

新知识:

解题过程

思考

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

难绷,两条路堵死了

题解分析

参考题解链接:​​​​​​​前后缀分解,附题单!

关键在理解

answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积。换句话说,如果知道了 i 左边所有数的乘积,以及 i 右边所有数的乘积,就可以算出 answer[i]。

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    let n=nums.length,res=[]
    let left=Array(n).fill(1)
    let right=Array(n).fill(1)
    for(let i=1;i<n;i++){
        left[i]=left[i-1]*nums[i-1]
    }
    res[n-1]=left[n-1]
    for(let i=n-2;i>=0;i--){
        right[i]=right[i+1]*nums[i+1]
        res[i]=right[i]*left[i]
    }
    return res
};

再改进一下,设三个数组太麻烦了,可不可以只设置一个,改进如下


/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    let n=nums.length
    let res=Array(n).fill(1)
    for(let i=1;i<n;i++){
        res[i]=res[i-1]*nums[i-1]
    }
    let right=1
    for(let i=n-1;i>=0;i--){
        res[i]=res[i]*right
        right=right*nums[i]
    }
    return res
};

手搓答案(无非废话版)

总结

 注意怎么改能把三个数组改成1个

 ​​​​​​​41. 缺失的第一个正数

题目链接:​​​​​​​41. 缺失的第一个正数

难度:困难

刷题状态:1刷

新知识:

解题过程

思考

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

重点是不能用这个 nums.sort((a,b)=>a-b),不能排序

注意可能出现重复的数字

搞不出来看答案

题解分析

参考题解链接:​​​​​​​缺失的第一个正数

第一:当nums[i]>n的时候,不用去考虑

比如nums = [7,8,9,11,12],n=5,排满的情况下[1,2,3,4,5],所以7,8,9,,,根本不用考虑

第二:假设nums[i]就应该待在nums[nums[i]-1]的位置,

nums[i]是值a,他现在的位置是i,他应该在的位置是nums[i]-1

但是现在在nums[i]-1位置上的是nums[nums[i]-1]值b,要交换ab的值,是值a呆在nums[i]-1位置上

也就是nums[i]=nums[nums[i]-1]

就是说理想的情况是[1,2,3,4],然后出现了[1,1,3,4],那么就可以判断2是缺失的

详细分析如下

第三,要用while而不是if,因为被换过来的值b现在待在i位置上,但b应该在的位置是b-1,两者不一定相等,所以要循环直到经历过该位置的数字都各归各位

,代码如下

var firstMissingPositive = function(nums) {
    let n=nums.length
    for(let i=0;i<n;i++){
        while(nums[i]>0&&nums[i]<=n&&nums[nums[i]-1]!=nums[i]){
            let tmp=nums[nums[i]-1]
            nums[nums[i]-1]=nums[i]
            nums[i]=tmp
        }
    }
    for(let i=0;i<n;i++){
        if(nums[i]!=i+1){
            return i+1
        }
    }
    return n+1
};

手搓答案(无非废话版)

var firstMissingPositive = function(nums) {
    let n=nums.length
    for(let i=0;i<n;i++){
        while(nums[i]>0&&nums[i]<=n&&nums[nums[i]-1]!=nums[i]){
            let tmp=nums[nums[i]-1]
            nums[nums[i]-1]=nums[i]
            nums[i]=tmp
        }
    }
    for(let i=0;i<n;i++){
        if(nums[i]!=i+1){
            return i+1
        }
    }
    return n+1
};

总结

这题太巧妙了,虽然写出来就几行,但逻辑上要拐几个弯(头凸)

 

 

相关文章:

  • Sqlserver安全篇之_TLS的证书概念
  • 【LeetCode】131.分割回文串
  • Spring全面详解(基础版)
  • 【uniapp原生】实时记录接口请求延迟,并生成写入文件到安卓设备
  • Linux的诞生:一场自由与协作的技术革命
  • DevOps原理和实现面试题及参考答案
  • Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存
  • 自动驾驶测试场景相关概念
  • 【深度学习】循环神经网络案例讲解-基于 PyTorch 的中文歌词生成——RNN,LSTM,GRU 从数据预处理到预测全流程详解
  • 字符函数和字符串函数1
  • Java实现pdf文件压缩(aspose-pdf实现压缩、itextpdf去除aspose-pdf版权水印)
  • 设计模式——过滤器模式在 Spring 中的实践
  • 深度学习开源数据集大全:从入门到前沿
  • Tr0ll: 1靶场渗透测试
  • 基于JAVA+SpringBoot+Vue的前后端分离的职称评审管理系统
  • mamba_ssm和causal-conv1d详细安装教程
  • 【Spring IoC】容器和IoC介绍以及IoC程序开发的优势
  • django.core.exceptions.ValidationError
  • flink介绍
  • 【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目