社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
从今天起,为期7天LeetCode刷题记录。
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
那么我们就可以给出我们的解答,直接暴力:
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
if target < 2:
return None
for i in range(0,len(nums)-1):
for j in range(i+1,len(nums)):
if nums[i] + nums[j] == target:
return i,j
最后得出来的结果是10264 ms,好吧,双重for循环挺容易想的,但复杂度也高。然后我马上就想到了第二种:
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
if not nums:
return None
i = 0
while i < len(nums):
j = i + 1
difference = target - nums[i]
while j < len(nums):
if nums[j] == difference:
return i, j
j += 1
i += 1
结果是9564 ms。。。只比第一种好那么一点点,但我觉得比第一种考虑的东西多了很多,另外复杂度我本来想着是能降两个千的。可惜,好像还是暴力了,感觉太年轻。。。然后去看了下大佬的代码,发现要用字典做:
class Solution:
def twoSum(self, nums, target):
if not nums:
return None
d = dict()
for i, item in enumerate(nums):
tmp = target - item
if tmp in d:
return [i, d[tmp]]
d[item] = i
return None
总结:这次时间上来讲,有点急,家里有些事,晚上才开始刷,白天在弄美赛和另一篇博文,所以导致两种方法间隔半小时,接下来要好好规划一下时间了。
很抱歉的是,2019/1/27号并没有刷题,由于家里有一些饭局以及美赛已经到了比较刺激的时候,虽然最后还是划了一天的水,但我竟然用2016年小区模拟的MATLAB自动机代码去模拟了一条龙的生长,至于生长公式,那不就跟造火箭是一个性质的嘛。。。最后结果跑是跑出来的,但完全不知道是个啥,守恒是不可能了,爱因斯坦也阻止不了龙族崛起。
最后写了个段子,参考知乎热榜2019年美赛是种什么体验:吸了一天的毒,又养了一天的龙,发现龙变成了喷火龙,并飞向了巴黎,所以,我想带你去浪漫的卢浮宫看一场恐怖袭击,顺便运用无人机进行空中救灾演习,虽然我仅仅只有虚拟货币,但还是想维持生态稳定性。
言归正传,其实这次美赛我没有参加,只是有种遗憾和情怀想再去玩一下而已,中途效率起伏不定,家里事也比较多,基本也算半玩半做吧,但目前已经没思路了,准备撤了,另外补齐一下昨天的刷题吧:
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
这题可以写下分析,首先看到题目后面的难度为困难,说实话确实有点怕,代码水平不高,除了蓝桥杯还没做过困难的题,但没办法,眼看着又到交稿日期了,只能开干了,关于时间复杂度O(log(m + n)),不懂,没有学过数据结构,之前在一篇博文里总结过栈、队列和链表,我又回头看了一下,嗯,没啥用处。但时间复杂度是啥,没有太多概念,不过我好像在pythontop中刷过字符串转中位数的题,那么本题就有第一种解法了:
没错,python里面还有更好的方式和归并等同,用一个内置函数,它里面的逻辑其实是用快排做的,那就是sort。如果忘了的朋友,可以看下我下面的这篇博文,不过这篇博文里我好像没提到这一点:
python内置函数总结与思维导图
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums1.extend(nums2) # 合并
add = nums1
if not add:
return None
add.sort() # 改变原来的列表
if len(add) % 2: # 计数不是循环。
return add[len(add)//2] / 1.0
else:
return (add[len(add)//2] + add[(len(add)//2)-1]) / 2.0
总结:这个代码总共提交了3次,每次结果都不一样,最好在112ms,这也是排序的特性。另外需要注意sort和sorted的区别,如果是sorted需要赋给新的变量。然后我提交完后去查了下快排的时间复杂度,发现是O(nlogn),那么本题就应该是O((n+m)log(n+m))了,好像和题意有些出入?。。不太清楚,官方题解没有看懂,但感觉递归法应该可行,不过我暂时没想了,有些朋友一年没见了,今天天气也还好,要去碰个面,晚上回来可能继续造龙?。。。感觉有点悬,还是吸个毒造个火箭吧。(手动笑脸)
嗯,要开始认真刷题了,最近一些事情算忙完了,接下来又是新的开始。那么话不多说,开始今天的题。
给定一个字符串
s
,找到s
中最长的回文子串。你可以假设s
的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
题目意思相比于上题来讲,算很清楚了,但清楚的题目往往都不是很好做。然后鉴于现在时间还算充裕,我不知道这题对于暴力求解会不会超时,但暂时没想用了,有时候一味暴力容易陷入死局,大概看了一下官方题解,这个也比上题清晰,甚至有点太过笼统。。从动态规划到中心扩展算法,最后是Manacher,嗯,不懂。然后今天主要研究了一下动态规划。下面为分析:
可能本题我有点跑偏,因为之前确实没怎么看过动态规划这方面的东西,但无关大雅,作为动态规划里最经典的案例之一,最大公共子序列(Longest Common Subsequence,简称LCS),是从两个公共的序列中提取相同的子序列,子序列是指从原序列中任意去掉若干元素(不一定连续)的序列,本题因为是字符串,那么我们两段序列也为字符串,举个例子: S S S 为"abccade", J J J 为"dgcadde",那么它们的最长公共子串即为"cad"。
对于上面这个例子,可能我们想到的最简单的方法是采用蛮力法,假设字符串 S S S 与 J J J 的长度分别为 l e n 1 len1 len1 和 l e n 2 len2 len2 (假设 l e n 1 len1 len1 >= l e n 2 len2 len2 )。那么可以先找出 J J J 的所有可能子串,然后判断这些子串是否也是 S S S 的子串,但效率非常低下,原因是比较次数过多,那么动态规划就是一种能通过减少比较次数从而降低时间复杂度的方法。
那么,什么是动态规划?动态规划算法通常基于一个递推公式及一个或多个初始状态。当前子问题的解将由上一次子问题的解推出。使用动态规划来解题只需要多项式时间复杂度,因此它比回溯法、暴力法等要快许多。
根据上面的意思,如果要运用动态规划来解决这个问题,首先我们要构造递归关系。假设 L C S [ i , j ] LCS[i,j] LCS[i,j]为序列 S [ 1.. i ] S[1..i] S[1..i] 和 J [ 1.. j ] J[1..j] J[1..j] 的LCS长度,那么我们是否可以用更小的实例来求解 L C S [ i , j ] LCS[i,j] LCS[i,j] 呢?我们可以减少序列的长度,可能的子问题是 L C S [ i − 1 ] [ j ] , L C S [ i ] [ j − 1 ] , L C S [ i − 1 ] [ j − 1 ] LCS[i-1][j], LCS[i][j-1], LCS[i-1][j-1] LCS[i−1][j],LCS[i][j−1],LCS[i−1][j−1],那么 L C S [ i ] [ j ] LCS[i][j] LCS[i][j]是否与这些子问题有关呢?这里还需要考虑 S [ i ] S[i] S[i]与 J [ j ] J[j] J[j],我们分情况讨论:
由此,我们可以根据上面的式子得到
L
C
S
[
i
]
[
j
]
LCS[i][j]
LCS[i][j]所有的值,进而找出最长的子串,另外关于上面我举的
S
S
S 为"abccade"、
J
J
J为"dgcadde"的例子,我们可以画出动态规划的计算结果,为:
可能有点小丑,但无伤大雅。通过上图所示,max为最长公共子串的长度,以及maxIndex为最长子串结尾字符在字符数组中的位置,由这两个值就可以确定最长公共子串为"cad",实现代码参考python算法面试宝典,如下:
"""
方法功能:获取两个字符串的最长公共字串
输入参数:str1和str2为指向字符的引用(指针)
"""
def getMaxSubStr(str1,str2):
len1 = len(str1)
len2 = len(str2)
SJ = ''
maxs = 0 # 用来记录最长公共子串的长度
maxI = 0 # 用来记录最长公共字串最后一个字符的位置
"""申请新的空间来记录公共字符长度信息"""
M = [[0 for i in range(len1 + 1)] for j in range(len2 + 1)]
"""利用递归公式构建二维数组"""
i = 0
while i < len1 + 1:
M[i][0] = 0
i += 1
j = 0
while j < len2 + 1:
M[0][j] = 0
j += 1
i = 1
"""动态规划推导"""
while i < len1 + 1:
j = 1
while j < len2 + 1:
if list(str1)[i-1] == list(str2)[j-1]:
M[i][j] = M[i-1][j-1] + 1
if M[i][j] > maxs:
maxs = M[i][j]
maxI = i
else:
M[i][j] = 0
j += 1
i += 1
"""找出公共子串"""
i = maxI - maxs
while i < maxI:
SJ = SJ + list(str1)[i]
i += 1
return SJ
参照上面的理解,那么我可以将
J
J
J变为
S
S
S的逆序,图解为:
意思是一样的,然后我改完代码之后在本地跑是可以实现的,除了时间长点以外,但我带入LeetCode提交后它显示一些莫名的报错信息,输入babad,我的代码输出结果为a?如果是爆内存还说得过去,但这判定好像有问题吧。。本地为bab,一切正常,我。。无话可说咯
中途看了下光城的微信公众号对于本题的理解以及代码,然后我再加上了点我自己的理解,那么分析如下:
如果要判断一个字符串是否是一个回文子串,还是可以参照上面我画的图,这里会有三种情况:
第一种:当所检测的子串长度为1时,即行列相等,那么可以说这是一个回文数;
第二种:当所检测的子串长度为2时,只需要判断当前与下一个元素是否相等即可确定该子串是否是回文串;
第三种:当所检测的子串长度为3即以上时,直接以3为跨步,便可以访问到每个子串的末尾字符,那么当当前位置的字符与末尾字符相等时,并且通过访问之前已经存储过的True or False进行比对,我们可以得到状态转移方程为:
d
p
[
i
]
[
j
]
=
{
t
r
u
e
,
s
t
r
[
i
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!