0%

前文

最近发的博客都太硬了,发个博客要写好久,加之最近时间非常紧张,很多博客都是开了个题就留下坑不填了。不过日语学习也是要坚持的,今天发一个轻松点的,关于日语音调的问题。就是学单词的时候看到后面的 ①②③④⑤ 。知识点比较简单,很容易掌握。

参考文献:

阅读全文 »

前言

VBS,即 Visual Basic Script,一种 Windows 环境下的脚本语言,可以被用于编写宏。因此宏病毒也需要使用VBS语言编写。由于编写简单,宏病毒的入门难度也很低。最近的作业要求学习使用 VBS做简单的操作,这里记录一些 VBS 的简单语法。

参考文献:

阅读全文 »

前言

最近真的是疯狂挖坑啊…… 看到什么都想学,只要和专业课无关或者只是他们的扩展就学的十分上头哈哈哈,人类神奇的心理。经过这学期比较硬核的专业课的学习(尤其是软件安全),也在反思自己是否在学习自己喜欢的东西,是否还有刚上大学,刚学编程语言时的那种兴奋感与快乐。之前受到各个方面的影响,总觉的学了安全以后的就可以当大黑阔,如看门狗中的能力,低头看一眼手机,周围的设备仅在掌控,实际上哪里有那么容易,挖到一个 0 day 漏洞背后不知道是多少基本功上的努力,看了多少汇编(令人头秃),而这类人才是非常非常稀有的,就像再次高考筛选一波 985 和清北的感觉一样。

最近学信息内容安全,之前第一反应是这个课大概就是监督舆论之类,抱着蹭 GPA 的心态来,不过学了以后发现是这个学期最为惊喜的一门课了。涉及到了机器学习、NLP、还有爬虫等很多之前就想试试看的领域。而在课程中,老师强推了 CS224n,大概看了以后感觉自己好菜啊……所以最近也在准备的了解一波基础知识。除去这些,其实个人对语言还是蛮感兴趣的,所以对NLP也有一点好奇,于是又找到了尘封多年的 吴恩达老师的机器学习 公开课开始学习。

课件相关资料:

阅读全文 »

前言

最近的课程有一些 NLP 的内容,为了卑微地跟上课程的进度只好从头安装 PyTorch 以及一些相关的组件,准备入门一些基本操作。

阅读全文 »

这几日的 LeetCode 都未搬运到博客上,恰好今天这个题有点意思,就新开一篇博文做个连载吧。(强行增加博文数目)

892. 三维形体的表面积

这一篇的题意是真的稀里糊涂很难懂啊,最后看了大神的评论解读才清晰多了。为了吐槽先扔原题题目。

在 N * N 的网格上,我们放置一些 1 * 1 * 1 的立方体。

每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。

请你返回最终形体的表面积。

示例 1:

输入:[[2]]
输出:10
示例 2:

输入:[[1,2],[3,4]]
输出:34
示例 3:

输入:[[1,0],[0,2]]
输出:16
示例 4:

输入:[[1,1,1],[1,0,1],[1,1,1]]
输出:32
示例 5:

输入:[[2,2,2],[2,1,2],[2,2,2]]
输出:46

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/surface-area-of-3d-shapes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

哈哈哈一开始着实是没看懂输入,但是其实输入的意思是,输入list的每个元素都是一列(如果俯视的话),然后每列的元素分别是这一列每一行的高度。例如 [[1,2],[3,4]] 表示(0,0)有1个,(0,1)有2个, (1,0) 有3个,(1,1) 有4个。

然后最直接的思路遍历正方体,然后看看空间上6个面有没有正方体,然后加上他贡献的总面积即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def surfaceArea(self, grid: List[List[int]]) -> int:
# [[1,2],[3,4]] 表示(0,0)有1个,(0,1)有2个, (1,0) 有3个,(1,1) 有4个
ans = 0
for col in range(len(grid)):
for row in range(len(grid[col])):
for height in range(1, grid[col][row] + 1):
# 正在先 x 后 y 最后 z 轴遍历每个小立方体
surface_area_to_add = 6 # 六个面没有立方体的正方体可以贡献6个表面积
if height + 1 < grid[col][row] + 1: # 空间上方有正方体
surface_area_to_add -= 1
if height != 1: # 空间下方有立方体
surface_area_to_add -= 1
if col != 0 and len(grid[col - 1])>= len(grid[col]) and grid[col-1][row] >= height:
surface_area_to_add -= 1
if col != len(grid) - 1 and len(grid[col + 1])>= len(grid[col]) and grid[col+1][row] >= height:
surface_area_to_add -= 1
# 因为遍历时,如果遍历到grid[col][row],就说明一定有 grid[col][row-1], 参考循环结构
if row != 0 and grid[col][row-1] >= height:
surface_area_to_add -= 1
if row != len(grid[col]) - 1 and grid[col][row+1] >= height:
surface_area_to_add -= 1
ans += surface_area_to_add
return ans

最后空间和时间都只超过了5%…… 其实上面的代码写到一半就不想写了,感觉太不优雅了,而且读起来也很绕。想到一个表面积其实就是6个面投影的总大小,而且2个对着的面的投影面积还是一样的,所以只用遍历3个角度(想起来三视图了)。这种思路是错误的,没有考虑空心的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 错误解法!!!
class Solution:
def surfaceArea(self, grid: List[List[int]]) -> int:
# [[1,2],[3,4]] 表示(0,0)有1个,(0,1)有2个, (1,0) 有3个,(1,1) 有4个
ans = 0
row_max_dict = {} # 记录每一行中最高高度的dict,因为50个元素开
for col in range(len(grid)):
for row in range(len(grid[col])):
if row not in row_max_dict or row_max_dict[row] < grid[col][row]:
row_max_dict[row] = grid[col][row]
if grid[col][row] != 0:
ans += 1 # 俯视角度的面积
ans += max(grid[col]) # 向 x 轴正方形看的角度的面积

for k in row_max_dict:
ans += row_max_dict[k] # 向 y 轴正方形看的角度的面积
ans *= 2
return ans

给出一个速度第二快而且思路非常清晰的解法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
def surfaceArea(self, grid: List[List[int]]) -> int:
res, N = 0, len(grid)
for i in range(N):
for j in range(N):
if grid[i][j]:
res += grid[i][j] * 4 + 2
if i > 0:
if grid[i][j] > grid[i-1][j]:
res -= grid[i-1][j] * 2
else:
res -= grid[i][j] * 2
if j > 0:
if grid[i][j] > grid[i][j-1]:
res -= grid[i][j-1] * 2
else:
res -= grid[i][j] * 2
return res
# 这个版本的思路 加减分明
# + 如果网格有方块 加上上下2 四面v*4
# - 如果是有 相邻的摆放 减去重叠的部分 较小的v*2
阅读全文 »

已经刷了 3 天的 LeetCode 每日一题,感觉还是相当的有趣的。但是为了避免自己把查到的知识不久又忘掉,将结题的一些笔记记录在这里。

543. 二叉树的直径

2020年3月10日每日一题。要求在在一棵二叉树中找到直径,所谓直径就是任意两个节点的最大距离。值得一提的是,直径不一定过根节点,看评论好多人都被这个迷惑了。

感觉题目是很典型的分治思想(虽然还没有好好学过算法就开始大用专业名词了),我们最终是记录每个节点以及其两个子树的整体内的直径;因为对于一棵二叉树,其直径只有可能是:

  • 左子树的直径(最大距离根本没有过根节点)
  • 右子树的直径(最大距离根本没有过根节点)
  • 左子树的深度 + 右子树的深度。(因为既然包括了根节点,那么左边子树中离根节点的最远距离就是左子树的深度,右子树同理,两边连起来正好是两边深度相加)

另外刚刚在搜索资料时甚至对树的深度的定义都开始犹豫,哈哈哈数据结构还给书本了。

定义一棵树的根结点层次为1,其他结点的层次是其父结点层次加1。一棵树中所有结点的层次的最大值称为这棵树的深度。

但是归根结底,任何的直径一定是某个节点的 左子树的深度 + 右子树的深度。我们只要一路遍历节点,记录最大值。顺便求出来左子树和右子树的深度即可。所以其实这个问题的核心就转向了求深度。深度直接递归调用自身,然后将左右子树返回结果各自加一,取最大值再返回即可。

另外,对于输入为 [] 的情况,也就是空树,需要 return -1, 否则会出 Exception。

最终 AC 代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None

class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
if root == None: # None Case
return 0
result = -1
def depth(node: TreeNode):
nonlocal result
left = 0
right = 0
if node.left != None:
left = depth(node.left) + 1
if node.right != None:
right = depth(node.right) + 1
result = max(left + right, result)
return max(left, right)
depth(root)
return result

1013. 将数组分成和相等的三个部分

2020年3月11日每日一题。给定一个整数数组,判断该数组是否能找到一种分割,使得分出的三个部分和相等。(顺序不可变化)

由于我们知道如果和不能是 3 的倍数的话,肯定是不能将整型 List 分割成和相等的三份的。所以先判断一下数组的和,然后如果模 3 为 0 的话,就继续并顺便保存一下 sum / 3。因为分出的3个段肯定和都是这个总和的三分之一。接下来遍历数组,每遍历一个就加上他的值,如果加上以后恰好为前面提到的 1/3 ,就清空这个计数,然后记录一下有一段满足条件。只要找到了2端并且后面还有数组元素,就可确定后面的和也必定是之前的1/3 ,停止遍历。

值得一提的是上面的方法经过了优化,其实我最初的想法是遍历完 List,统计和等于1/3 sum 的段,只有这个段的数目正好为 3 时,返回 true,否则就是 false。但是这种算法在遇到输入 为 [1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1] 的时候就出错了,因为算法找到了好几个和等于 1/3 sum (即 0 ) 的段。其实正确的答案是 True,但是算法会返回 False。遇到报错以后我的最直接想法是,直接判断是否大于 3 个段,且 1/3 sum == 0 。因为如果是 0 的话就可以任意组合。这样改算法以后也可以 AC 。但是看了评论后才知道了最精巧的解法,也就是前面一开始说的思路,只要找到 2 端和等于 1/3 的段,且后面还有元素,就说明可以了,可以避免刚刚和为 0 的输入出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def canThreePartsEqualSum(self, A: List[int]) -> bool:
ListSum = sum(A)
if ListSum % 3 != 0:
return False
oneThird = ListSum / 3
tmpSum = 0
validPart = 0
for num in A:
if validPart == 2:
return True
tmpSum += num
if tmpSum == oneThird:
validPart += 1
tmpSum = 0
return False
阅读全文 »

前言

最近到了暑期实习的招聘期,看着小伙伴们准备各厂的面试,刷题刷算法刷数据结构,突然感觉自己好菜啊。一说红黑树,就记得数据结构讲过,到底是什么都忘得干干净净。至于算法本身也没有好好学习过,只懂得一些基本的递归的思路。(不过这学期有选算法课,希望能好好学哈哈哈)

虽然目前的定位不是就业,但是感觉迟早要去产业界的话多刷刷题还是没有缺点的,而且刷题的过程也蛮好玩的(主要原因)。于是注册了一个 LeetCode 账号。这里发现好像国区有专属的服务,国内想去国际版的 LeetCode 貌似还很麻烦,找了很多对比,最后还是进了国区。随便试试一道题,发现居然支持十多种语言,连最近在学的 Kotlin 都有(TIOBE 排行34 的语言也太有面子了)。然鹅具体要用什么语言的时候却开始选择困难了,因为自己学语言基本都是入了门就没往深学了,都是半吊子,刷 LeetCode 的话还是希望能够让自己有一门非常熟悉的语言。Java 常年最流行语言,产业界常青藤,开发后盾、开发App、乃至web都会用到; Python 这些年也十分火热,写的方便,还有近些年大火的机器学习;作为安全专业科班出身,C、C++十分差劲也说不过去;尤其科研上准备往浏览器和 Web 方向前进, JavaScript, PHP 也要加深掌握。然后头秃了。最后思前想后还是打算做了 Python,毕竟现在 Python 和机器学习数据分析的需求量还是相当猛的(参考最近看到的各类招聘)。于是就有了这一篇博文。然后又惊喜的发现,最近刷的很舒服的 JetBrains Academy 有 python 的课程。于是打算先把 Kotlin 的这个项目刷完就去做 Python,然后尽可能坚持用 Python 刷 LeetCode。

参考资料

阅读全文 »

前言

本篇继续上一篇 Kotlin 的技术笔记,继续记录一些学习过程中觉得有意思或者比较重要的知识点。

本篇的主要内容来自于笔者在 JetBrains Academy 中所学到的部分。

https://hyperskill.org/ 为 JetBrains Academy 的网站,亦可以通过 IDEA 的 EduTools 来参加学习。

其余有许多优秀的参考资料已经列于上一篇 Kotlin 的博文,如在本篇中有重复使用将额外列出。

本篇博文假设读者具有基本的 Java 编程知识。

参考资料

https://www.cnblogs.com/Jetictors/p/9237108.html 一个很不错的讲解 Kotlin 的博文系列

https://blog.csdn.net/lckj686/article/details/80448471 Kotlin 中 复合符号( ‘?.’ ‘?:’ ‘!!’ ‘as?’ ‘?’ )

阅读全文 »

前言

PE 是Windows 平台上最主流的可执行文件格式。

可以在 winnt.h 中找到 PE 结构的定义。

EXE 与 DLL 其实使用的是一样的 PE 结构,只是在一个字段的内容不同,同时 DLL 拥有一些自己的扩展。

64位的 Windows 可执行程序使用的格式叫做 PE32+ , 没有新的结构加入。对很多字段进行了长度扩展。

当 PE 文件被装载入内存后,内存中的版本就叫 模块(Module)。映射文件的起始地址叫做模块句柄(hModule)。这个初始地址也叫基地址 ImageBase。

按照默认配置 VC++ 建立的 EXE 基地址 40 0000H , DLL 为 1000 0000H。

虚拟地址 VA = 基地址 ImageBase + 相对虚拟地址 RVA

PE 文件在磁盘中时,某个数据的位置相对文件开头的偏移量叫做 文件偏移地址 File Offset,或物理地址 RAW Offset。使用十六进制编辑器打开文件时显示的地址就是文件偏移地址。

如果编写解析 PE 的程序,读取其内部相关内容(输入表、输出表等),不能将区块名称做为参考。正确的方法是按照数据目录表中的字段进行定位。

INT 和 IAT 在磁盘中是一样的。在加载入内存后,INT 中的内容不变, IAT 中的每一项被改写为该函数的地址。

参考资料:

  • 《加密与解密》
阅读全文 »