携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
729. 我的日程安排表 I
难度中等224收藏分享切换为英文接收动态反馈
实现一个 MyCalendar 类来存放你的日程安排。如果要添加的日程安排不会造成 重复预订 ,则可以存储这个新的日程安排。
当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订 。
日程可以用一对整数 start 和 end 表示,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end 。
实现 MyCalendar 类:
MyCalendar()初始化日历对象。boolean book(int start, int end)如果可以将日程安排成功添加到日历中而不会导致重复预订,返回true。否则,返回false并且不要将该日程安排添加到日历中。
示例:
输入:
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
输出:
[null, true, false, true]
解释:
MyCalendar myCalendar = new MyCalendar();
myCalendar.book(10, 20); // return True
myCalendar.book(15, 25); // return False ,这个日程安排不能添加到日历中,因为时间 15 已经被另一个日程安排预订了。
myCalendar.book(20, 30); // return True ,这个日程安排可以添加到日历中,因为第一个日程安排预订的每个时间都小于 20 ,且不包含时间 20 。
提示:
0 <= start < end <= 109- 每个测试用例,调用
book方法的次数最多不超过1000次。
代码
from sortedcontainers import SortedDict
class MyCalendar:
'''
由题意可得,如果我们按照时间顺序存储日程安排,那么就省去了排序这一过程,并且查询可以使用二分
这里使用SortedDict来维护一个有顺序的日程安排
'''
def __init__(self):
self.books = SortedDict()
def book(self, start: int, end: int) -> bool:
# 这里我们按照end进行左查找,也可以按照start进行右查找,不过边界条件的判断会稍微的麻烦些
idx = self.books.bisect_left(end) # 查找到字典键值所在位置或者键值应该插入的位置
if idx == 0 or self.books.items()[idx-1][1] <= start:
# 这里做下解释,第一种情况如果idx == 0,说明end小于时间顺序中任何一个键值,直接插入
# 第二种情况,因为查找的是键值所在位置,那么end后面的时间安排开始时间一定是 >= end的
# 所以只用考虑start的值和前一个日程安排的位置的结束时间,如果结束时间 <= start说明满足条件
# 入字典,返回True
self.books[start] = end
return True
return False
# Your MyCalendar object will be instantiated and called as such:
# obj = MyCalendar()
# param_1 = obj.book(start,end)
视频
648. 单词替换
难度中等247收藏分享切换为英文接收动态反馈
在英语中,我们有一个叫做 词根(root) 的概念,可以词根后面添加其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。
现在,给定一个由许多词根组成的词典 dictionary 和一个用空格分隔单词形成的句子 sentence。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。
你需要输出替换之后的句子。
示例 1:
输入: dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
输出: "the cat was rat by the bat"
示例 2:
输入: dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs"
输出: "a a b c"
提示:
1 <= dictionary.length <= 10001 <= dictionary[i].length <= 100dictionary[i]仅由小写字母组成。1 <= sentence.length <= 10^6sentence仅由小写字母和空格组成。sentence中单词的总量在范围[1, 1000]内。sentence中每个单词的长度在范围[1, 1000]内。sentence中单词之间由一个空格隔开。sentence没有前导或尾随空格。
代码
class Solution:
def replaceWords(self, dictionary: List[str], sentence: str) -> str:
st = set(dictionary) #建立集合来表明有多少词根,注意词根只是开头,后面遍历会用到
sents = sentence.split(" ")
for i in range(len(sents)): # 挨个取单词
for j in range(len(sents[i])): # 从前向后取词根,即前缀
if sents[i][:j] in st:
# 嘶~这里好像字典解决不了,逆向思维一下,我们不把字典的值赋给他就好了
sents[i] = sents[i][:j] # 这样一定可以解决
return " ".join(sents)
873. 最长的斐波那契子序列的长度
难度中等332收藏分享切换为英文接收动态反馈
如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:
n >= 3- 对于所有
i + 2 <= n,都有X_i + X_{i+1} = X_{i+2}
给定一个严格递增的正整数数组形成序列 arr ,找到 arr 中最长的斐波那契式的子序列的长度。如果一个不存在,返回 0 。
(回想一下,子序列是从原序列 arr 中派生出来的,它从 arr 中删掉任意数量的元素(也可以不删),而不改变其余元素的顺序。例如, [3, 5, 8] 是 [3, 4, 5, 6, 7, 8] 的一个子序列)
示例 1:
输入: arr = [1,2,3,4,5,6,7,8]
输出: 5
解释: 最长的斐波那契式子序列为 [1,2,3,5,8] 。
示例 2:
输入: arr = [1,3,7,11,12,14,18]
输出: 3
解释: 最长的斐波那契式子序列有 [1,11,12]、[3,11,14] 以及 [7,11,18] 。
提示:
3 <= arr.length <= 10001 <= arr[i] < arr[i + 1] <= 10^9
代码
class Solution:
def lenLongestFibSubseq(self, arr: List[int]) -> int:
# 动态规划
# 我们假设dp[i][j]是arr第i个位置的数与第j个位置的数可以组成子序列
# 为了方便表示我们将arr转化成一个位置与数值对应的字典
idx = {x:i for i, x in enumerate(arr)} # {数值:索引},用于后边由数值直接找到数值位置
# 注意一下1 <= arr[i] < arr[i + 1] <= 10^9 并且严格递增的正整数数组
# 后面的一些操作和这两个特性有关
cnt = 0 # 计数器
n = len(arr)
dp = [[0] * n for _ in range(n)]
# dp是按照索引位dp的
for i, x in enumerate(arr):
if i != 0: # i是第一个的时候前面不会有数
for j in range(i-1, -1, -1):
# i从前向后,j从后向前,我们这里需要的是dp[j][i],并且j<i
# if arr[j] * 2 <= x:
# # 这时已经找不到在j之前的k了,如果能找到也一定是位于他们后边的了
# break
# 如果不这样修改还有其他修改方式
if x - arr[j] in idx: # 因为严格单调增,所以x-arr[j]一定>0
k = idx[x - arr[j]]
# 如果x - arr[j] 在字典里,说明k位置上的数与j,i位置上的数构成子序列
if k < j: # 这种修改可能更容易理解一点
dp[j][i] = max(dp[k][j]+1, 3)
#dp[j][i] 为k,j能组成的子序列数+1与3之间的最大值(一定存在k,j,i这三个)
cnt = max(cnt, dp[j][i])
return cnt
视频