class Solution(object):
def shortestDistance(self, words, word1, word2):
"""
:type words: List[str]
:type word1: str
:type word2: str
:rtype: int
"""
res = len(words) - 1
pos1, pos2 = -1, -1
for i, word in enumerate(words):
if word == word1:
pos1 = i
elif word == word2:
pos2 = i
if pos1 != -1 and pos2 != -1:
res = min(res, abs(pos1 - pos2))
return res
244. 最短单词距离 II
使用哈希表,key是words里的每个单词,val是每个word出现的所有下标
class WordDistance(object):
def __init__(self, words):
"""
:type words: List[str]
"""
self.words = words
self.record = defaultdict(list)
for i, word in enumerate(words):
self.record[word].append(i)
# print self.record
def shortest(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
res = len(self.words)
for pos1 in self.record[word1]:
for pos2 in self.record[word2]:
res = min(res, abs(pos1 - pos2))
return res
245. 最短单词距离 III
如果相等,那么就用一个变量记录上一次出现的下表即可。
class Solution(object):
def shortestDistance(self, words, word1, word2):
"""
:type words: List[str]
:type word1: str
:type word2: str
:rtype: int
"""
res = len(words) - 1
pos1, pos2 = -1, -1
if word1!=word2:
for i,word in enumerate(words):
if word==word1:
pos1=i
if word==word2:
pos2=i
if pos1!=-1 and pos2!=-1:
res=min(res,abs(pos1-pos2))
else:
for i,word in enumerate(words):
if word==word1:
if pos1!=-1:
res=min(res,i-pos1)
pos1=i
return res
121. 买卖股票的最佳时机
我们只需要遍历价格数组一遍,记录历史最低点,然后在每一天考虑这么一个问题:如果我是在历史最低点买进的,那么我今天卖出能赚多少钱?
如果暴力计算每天赚的钱,会超时
class Solution:
def maxProfit(self, prices: List[int]) -> int:
maxc=0
minprice=float('inf')
for i in range(len(prices)):
maxc=max(maxc,prices[i]-minprice)
minprice=min(minprice,prices[i])
return maxc
单调栈:
单调栈的应用场景 当你需要高效率查询某个位置左右两侧比他大(或小)的数的位置的时候
维护一个单调递增栈,每次弹出不符合的栈顶元素时,更新栈顶与栈底的差值,因为这个栈顶元素无论再大,后续也不会使用了
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#添加哨兵
prices.append(0)
maxp=0
stack=[]
for price in prices:
while stack and stack[-1]>price:
maxp=max(maxp,stack[-1]-stack[0])
stack.pop()
stack.append(price)
return maxp
122. 买卖股票的最佳时机 II
不限制交易次数,所有产生利润的天都买卖,利润为0或者负就不参与买卖
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit=0
for i in range(1,len(prices)):
if prices[i]-prices[i-1]>0:
profit+=prices[i]-prices[i-1]
return profit
123. 买卖股票的最佳时机 III
作者:labuladong
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/yi-ge-tong-yong-fang-fa-tuan-mie-6-dao-gu-piao-wen/
利用「状态」进行穷举。我们具体到每一天,看看总共有几种可能的「状态」,再找出每个「状态」对应的「选择」。我们要穷举所有「状态」,穷举的目的是根据对应的「选择」更新状态。
每天都有三种「选择」:买入、卖出、无操作,我们用 buy, sell, rest 表示这三种选择。但问题是,并不是每天都可以任意选择这三种选择的,因为 sell 必须在 buy 之后,buy 必须在 sell 之后。那么 rest 操作还应该分两种状态,一种是 buy 之后的 rest(持有了股票),一种是 sell 之后的 rest(没有持有股票)。而且别忘了,我们还有交易次数 k 的限制,就是说你 buy 还只能在 k > 0 的前提下操作
我们想求的最终答案是 dp[n - 1][K][0],即最后一天,最多允许 K 次交易,最多获得多少利润。读者可能问为什么不是 dp[n - 1][K][1]?因为 [1] 代表手上还持有股票,[0] 表示手上的股票已经卖出去了,很显然后者得到的利润一定大于前者。
dp状态转移方程表示的是利润
dp[i][k][0 or 1]表示第i天,最多可完成k笔交易时,是(1)否(0)持有股票时,的最大利润。
买股票的时候交易次数减一
如果 buy,就要从利润中减去 prices[i],如果 sell,就要给利润增加 prices[i]。今天的最大利润就是这两种可能选择中较大的那个
base case:
k从1开始,因为dp[i][0][0 or 1]表示最多可进行0笔交易,不论什么时候结果都是0
k表示最多进行的交易次数,dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
后面的k-1表示:当前最多进行K次交易,且此次已经进行了一次买入交易,那么之前最多进行k-1次交易
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]) ,第i天未持有:(1)第i-1天未持有,(2)第i-1天持有,第i天卖出,赚钱第i天利润
dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]) ,第i天持有:(1)第i-1天持有,(2)第i-1天未持有,第i天买入,买入时需要消耗一次k,成本为prices[i]
dp[0][k][1]=float("-inf") 第0天不可能有一次交易,设为负无穷float("-inf")
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#状态转移方程
#买入股票消耗交易次数
# 三个维度,第一维表示天,第二维表示交易了几次,第三维表示是否持有股票。
if not prices:
return 0
maxk=2
n=len(prices)
##最里面的[0,0]表示:第一位是未持有股票的利润,第二位是持有股票的利润
dp=[[[0,0]for i in range(maxk+1)]for j in range(n+1)]
#dp[i][0][0]=0:无论哪天,没有交易,没有持有股票,利润都为0,不用单独初始化
for k in range(maxk+1):
#第0天未持有股票,初始利润为0
dp[0][k][0]=0
#第0天不可能持有股票,初始化为负无穷
dp[0][k][1]=float('-inf')
#i从1开始,从第一天开始计数
#k表示最多进行的交易次数,1,2,
for i in range(1,n+1):
for k in range(1,3):
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i-1])
dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i-1])
return dp[-1][2][0]
188. 买卖股票的最佳时机 IV
一次交易由买入和卖出构成,至少需要两天。所以说有效的限制 k 应该不超过 n/2,如果超过,就没有约束作用了,相当于 k = +infinity。
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
if not prices:
return 0
#天数从第一天开始,最多交易次数从1开始
#买股票消耗交易次数
n=len(prices)
# 当允许交易的次数大于交易天数时,相当于是可以无限交易,使用贪心算法
if k>=n/2:
maxp=0
for i in range(1,n):
if prices[i]-prices[i-1]>0:
maxp+=prices[i]-prices[i-1]
return maxp
dp=[[[0,0] for _ in range(k+1)] for _ in range(n+1)]
for i in range(k+1):
#第0天不可能持有股票,负无穷表示不可能
dp[0][i][1]=float('-inf')
#第0天无股票利润为0
dp[0][i][0]=0
#dp[i][0][0]=0 :无交易次数,且不持有股票,利润为0
#dp[i][0][1]=float('-inf):无交易次数,不可能还持有股票,负无穷表示,这个初始化已经包含在上面的初始化
for i in range(1,n+1):
for j in range(1,k+1):
#当前不持有股票,可能之前也不持有或者之前持有股票,但是今天卖了
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]+prices[i-1])
#当前持有股票,可能之前也持有或者之前未持有股票,但是今天买了,消耗一次交易次数
dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-prices[i-1])
return dp[-1][k][0]
309. 最佳买卖股票时机含冷冻期
第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1 。
dp[i][1]可由两种情况得到:【i-1天本来就持有股票】和【i-1天没有股票,是今天进行了买入操作】。后者dp[i-1][0] - prices[i-1]由于是今天(i天)进行了买入操作,那么i-1天没有股票的原因就一定【不能是】i-1天进行了卖出操作(因为冷冻期)。即i-1天没有股票的原因一定是因为i-2天就没有股票。即dp[i-1][0] =dp[i-2][0] ,因此直接用dp[i-2][0]表示,就可以避免dp[i-1][0]中可能包含的上述违禁操作。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#交易不限次数
if not prices:
return 0
n=len(prices)
dp=[[0,0] for i in range(n+1)]
#初始化
dp[0][0]=0
dp[0][1]=float('-inf')
dp[1][0]=0
#dp[-1][1]=float('-inf):因为不可能存在
#dp[-1][0]=0:未开始交易,利润为0
dp[1][1]=-prices[0]
#因为有i-2,所以i从2开始
for i in range(2,n+1):
#因为有冷冻期,所以卖出和买入隔一天状态转移
#【i-1天没有股票,是今天进行了买入操作】。后者dp[i-1][0] - prices[i-1]由于是今天(i天)进行了买入操作,那么i-1天没有股票的原因就一定【不能是】i-1天进行了卖出操作(因为冷冻期)。即i-1天没有股票的原因一定是因为i-2天就没有股票。即dp[i-1][0] =dp[i-2][0] ,因此直接用dp[i-2][0]表示,就可以避免dp[i-1][0]中可能包含的上述违禁操作。
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1])
dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i-1])
return dp[-1][0]
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
if not prices:
return 0
n=len(prices)
dp=[[0,0]for _ in range(n+1)]
dp[0][0]=0
dp[0][1]=float('-inf')
#买入股票计入一次交易,每次交易从利润里减去手续费即可
for i in range(1,n+1):
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i-1]-fee)
return dp[-1][0]
898. 子数组按位或操作
一个cur集合储存上一次循环的位或结果集合,将这些集合和当前循环的元素进行位或运算,在补充上当前元素,然后结果集与cur求并集,可以添加进来
暴力求每个数字与后面数字的异或会超时
class Solution:
def subarrayBitwiseORs(self, A: List[int]) -> int:
cur={0}
res=set()
for i in A:
#cur保存上一次循环的位或结果集
#当前循环每个元素与cur或,最后添加当前元素进来
cur={i|y for y in cur}|{i}
res|=cur
return len(res)
面试题 05.06. 整数转换
python3中的整形,是以补码形式存在
正数的补码和原码相同
负数的补码是原码取反加1
python3中的负数,输出的是原码的二进制加一个负号,所以你为了获得负数(十进制表示)的补码,需要手动将其和十六进制数 0xffffffff 进行按位与操作,得到结果是个十六进制数,再交给 bin() 进行输出,得到的才是你想要的补码表示
class Solution:
def convertInteger(self, A: int, B: int) -> int:
#a^b :a,b不一样的位为1
#对负数来说,& 0xffffffff是转换成32位补码
tmp=(A&0xffffffff)^(B&0xffffffff)
count=0
while tmp!=0:
count+=1
#用来求Tmp中有几个1
tmp&=(tmp-1)
return count