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
};
总结
这题太巧妙了,虽然写出来就几行,但逻辑上要拐几个弯(头凸)