LeetCode刷题实录Python篇(六)

116 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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)

视频

www.bilibili.com/video/BV1wZ…

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 <= 1000
  • 1 <= dictionary[i].length <= 100
  • dictionary[i] 仅由小写字母组成。
  • 1 <= sentence.length <= 10^6
  • sentence 仅由小写字母和空格组成。
  • 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 <= 1000
  • 1 <= 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

视频

www.bilibili.com/video/BV19G…