前言
下班吃完饭洗完澡,9点半了开始学习!
第五天
关于动态规划,今天上班空闲的时候重新看了一下,感觉好像有点点小技巧又好像没有哈哈哈,应该是努力找一下f(n)和f(n-1)f(n-2)之间的关系等等,还是熟能生巧重要,不过昨天的正则表达式那道题确实难度有点大,建议先跳过做简单题,学习思路。
十四题
剑指 Offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"
思路: 假设数字是这样子拼接X1X2…Xn-1Xn。那么回顾一下我在前言底下说的,努力找一下f(n)和f(n-1)f(n-2)之间的关系,看到题意中的点数字可以分为一个一个的数字也可以分为两个两个的(0<=num<=25)。接下来分情况:
- 当一个一个翻译,Xn的时候,X1X2…Xn-1Xn,那么要求的其实就是到n-1位的时候有多少种翻译,那么就是求f(n-1)
- 当两个两个翻译,Xn的时候,X1X2…Xn-2Xn-1Xn,那么要求的其实就是到n-2位的时候有多少种翻译,那么就是求f(n-2)。
- 因为是求总和,所以f(n)=f(n-1)+f(n-2)。
yes终于找到了他们之间的关系。那么运用动态规划的思想,从0和1往上叠加。f(2)=f(1)+f(0)。我们可以看出来的f(2)最多为2,f(1)为1,所以说这里可以判定当第1,2位组成的数字范围在[10,25]之间的时候,2=1+f(0),可以得出f(0)=1,那么就可以开始写代码了。
function translateNum(num: number): number {
let a = 1 // 初始化f(0)
let b = 1 // 初始化f(1)
let sum = 1
const nums = num.toString().split('')
for(let i = 2;i<=nums.length;i++){
const firstNum = parseInt(nums[i-2])
const secondNum = parseInt(nums[i-1])
if(firstNum!==0&&(firstNum*10+secondNum)<=25){
sum = a + b
}
a = b
b = sum
}
return sum
};
有了这个思路之后好像清晰多了嘿嘿。
十五题
剑指 Offer 47. 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
思路: 这个题第一眼看到就让我想起了往日的走迷宫大作业哈哈哈。好了那么这一题呢就是像第三天动态规划中的分蛋糕事件一样。这一步的最大值=上一步的最大值+这一步的值。
function maxValue(grid: number[ ][ ]): number {
const lineNums = grid.length
const columnNums = grid[0].length
for(let i = 0;i<lineNums;i++){
for(let j = 0;j<columnNums;j++){
if(i===0&&j===0) continue
if(i===0) grid[i][j] += grid[i][j-1] //i=0,表示向右移动过了
else if(j===0) grid[i][j] += grid[i-1][j] //j=0,表示向下移动过了
else grid[i][j] += Math.max(grid[i-1][j],grid[i][j-1]) //在中间就挑大的走
}
}
return grid[lineNums-1][columnNums-1]
};
直接修改原数据已经成了这种题的一个经典答法哈哈,可以节省空间,而且顺手。不过还有可以改进的地方,看了解析之后(很庆幸自己的思路终于正确了TAT)解析这样说:
当 gridgrid 矩阵很大时,i=0 或 j=0 的情况仅占极少数,相当循环每轮都冗余了一次判断。因此,可先初始化矩阵第一行和第一列,再开始遍历递推。 如下:
function maxValue(grid: number[][]): number {
const lineNums = grid.length
const columnNums = grid[0].length
for(let j = 1;j<columnNums;j++) grid[0][j] += grid[0][j-1] //初始化第一行
for(let i = 1;i<lineNums;i++) grid[i][0] += grid[i-1][0] //初始化第一列
for(let i = 1;i<lineNums;i++){
for(let j = 1;j<columnNums;j++){
grid[i][j] += Math.max(grid[i-1][j],grid[i][j-1]) //就可以直接用了
}
}
return grid[lineNums-1][columnNums-1]
};
果然还是积累得不够哇加油加油。
十六题
剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
思路: 在数据结构中,我本人比较喜欢的就是哈希表。因为在实习的时候经常用到数组遍历遍历再遍历,而哈希表正是一个可以减少数组遍历时间复杂度的重要数据结构,所以学会它后我一直想要用它,这个题终于让我看到了机会。起初想用的是通过它的map.size的改变来判断有无新数据加入,可行是可行但是有些地方难以判断,所以最后在解析的帮助下非常简便地写了出来:
function lengthOfLongestSubstring(s: string): number {
const map:Map<string,number> = new Map()
let sign = 0
let max = 0
s.split('').forEach((item,index)=>{
const i = map.get(item)
map.set(item,index)
if(i!==undefined){//卡在这里一会,因为第一个数index是0 如果直接写if(i)的话i=0也会跑到else去
sign = sign<index-i?sign+1:index-i
}else{
sign++
}
max = Math.max(max,sign)
})
return max
};
今天比昨天进步了可多,还是比较欣慰哈哈,不过这个思维还是不够深入,明天还有三道题,不知道能不能做完,又1点了,雾,赶快睡觉晚安。
代码敲多了一定要起来走走喝水,要温故而知新。
下面是我的文章会引用到的作者和他的文章,都是跟着它学的,感谢。
作者:Krahets 链接:leetcode.cn/leetbook/re… 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。