leetcode刷题:最后一次

系列文章目录

leetcode刷题:第一周
leetcode刷题:第二周
leetcode刷题:二分查找
leetcode刷题:双指针
leetcode刷题:广度/深度优先搜索
leetcode刷题:递归 / 回溯
leetcode刷题:动态规划
leetcode刷题:最后一次


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本来是准备将leetcode刷题做成一个系列的,但这个时间安排实在有点麻烦,每日一篇有点太浪费时间精力,这个系列先停掉吧,题目自己做就不分享。

一、leetcode例题

考试的最大困扰度

"""
一位老师正在出一场由 n道判断题构成的考试,每道题的答案为 true (用 'T' 表示)或者 false (用 'F'表示)。老师想增加学生对自己做出答案的不确定性,方法是最大化有 连续相同结果的题数。(也就是连续出现 true 或者连续出现 false)。

给你一个字符串answerKey,其中answerKey[i]是第 i个问题的正确结果。除此以外,还给你一个整数 k,表示你能进行以下操作的最多次数:

每次操作中,将问题的正确答案改为'T' 或者'F'(也就是将 answerKey[i] 改为'T'或者'F')。
请你返回在不超过 k次操作的情况下,最大连续 'T'或者 'F'的数目。

示例 1:

输入:answerKey = "TTFF", k = 2
输出:4
解释:我们可以将两个 'F' 都变为 'T' ,得到 answerKey = "TTTT" 。
总共有四个连续的 'T' 。
示例 2:

输入:answerKey = "TFFT", k = 1
输出:3
解释:我们可以将最前面的 'T' 换成 'F' ,得到 answerKey = "FFFT" 。
或者,我们可以将第二个 'T' 换成 'F' ,得到 answerKey = "TFFF" 。
两种情况下,都有三个连续的 'F' 。
示例 3:

输入:answerKey = "TTFTTFTT", k = 1
输出:5
解释:我们可以将第一个 'F' 换成 'T' ,得到 answerKey = "TTTTTFTT" 。
或者我们可以将第二个 'F' 换成 'T' ,得到 answerKey = "TTFTTTTT" 。
两种情况下,都有五个连续的 'T' 。
"""


class Solution:
    def __init__(self):
        self.answerKey = "TTFTTFTT"
        self.k = 1

    def maxConsecutiveAnswers(self):
        answerKey = self.answerKey
        k = self.k

        # 算法部分
        def maxConsecutiveChar(ch):
            ans = 0
            left = 0
            sum = 0
            for right in range(len(answerKey)):
                sum += answerKey[right] != ch
                while sum > k:
                    sum -= answerKey[left] != ch
                    left += 1
                ans = max(ans, right - left + 1)
            return ans

        return max(maxConsecutiveChar('T'), maxConsecutiveChar('F'))

找到处理最多请求的服务器

"""
你有 k个服务器,编号为 0到 k-1,它们可以同时处理多个请求组。每个服务器有无穷的计算能力但是 不能同时处理超过一个请求。请求分配到服务器的规则如下:

第i(序号从 0 开始)个请求到达。
如果所有服务器都已被占据,那么该请求被舍弃(完全不处理)。
如果第(i % k)个服务器空闲,那么对应服务器会处理该请求。
否则,将请求安排给下一个空闲的服务器(服务器构成一个环,必要的话可能从第 0 个服务器开始继续找下一个空闲的服务器)。比方说,如果第 i个服务器在忙,那么会查看第 (i+1)个服务器,第 (i+2)个服务器等等。
给你一个 严格递增的正整数数组arrival,表示第i个任务的到达时间,和另一个数组load,其中load[i]表示第i个请求的工作量(也就是服务器完成它所需要的时间)。你的任务是找到 最繁忙的服务器。最繁忙定义为一个服务器处理的请求数是所有服务器里最多的。

请你返回包含所有最繁忙服务器序号的列表,你可以以任意顺序返回这个列表。



示例 1:



输入:k = 3, arrival = [1,2,3,4,5], load = [5,2,3,3,3]
输出:[1]
解释:
所有服务器一开始都是空闲的。
前 3 个请求分别由前 3 台服务器依次处理。
请求 3 进来的时候,服务器 0 被占据,所以它被安排到下一台空闲的服务器,也就是服务器 1 。
请求 4 进来的时候,由于所有服务器都被占据,该请求被舍弃。
服务器 0 和 2 分别都处理了一个请求,服务器 1 处理了两个请求。所以服务器 1 是最忙的服务器。
示例 2:

输入:k = 3, arrival = [1,2,3,4], load = [1,2,1,2]
输出:[0]
解释:
前 3 个请求分别被前 3 个服务器处理。
请求 3 进来,由于服务器 0 空闲,它被服务器 0 处理。
服务器 0 处理了两个请求,服务器 1 和 2 分别处理了一个请求。所以服务器 0 是最忙的服务器。
示例 3:

输入:k = 3, arrival = [1,2,3], load = [10,12,11]
输出:[0,1,2]
解释:每个服务器分别处理了一个请求,所以它们都是最忙的服务器。
示例 4:

输入:k = 3, arrival = [1,2,3,4,8,9,10], load = [5,2,10,3,1,2,2]
输出:[1]
示例 5:

输入:k = 1, arrival = [1], load = [1]
输出:[0]
"""
from heapq import heappop, heappush

from sortedcontainers import SortedList


class Solution:
    def __init__(self):
        self.k = 3
        self.arrival = [1, 2, 3, 4, 8, 9, 10]
        self.load = [5, 2, 10, 3, 1, 2, 2]

    def busiestServers(self):
        k = self.k
        arrival = self.arrival
        load = self.load
        # 算法部分

        available = SortedList(range(k))
        busy = []
        requests = [0] * k
        for i, (start, t) in enumerate(zip(arrival, load)):
            while busy and busy[0][0] <= start:
                available.add(busy[0][1])
                heappop(busy)
            if len(available) == 0:
                continue
            j = available.bisect_left(i % k)
            if j == len(available):
                j = 0
            id = available[j]
            requests[id] += 1
            heappush(busy, (start + t, id))
            available.remove(id)
        maxRequest = max(requests)
        return [i for i, req in enumerate(requests) if req == maxRequest]

if __name__ == '__main__':
    s=Solution()
    t=s.busiestServers()
    print(t)

自除数

"""
自除数 是指可以被它包含的每一位数整除的数。

例如,128 是一个 自除数 ,因为 128 % 1 == 0,128 % 2 == 0,128 % 8 == 0。
自除数 不允许包含 0 。

给定两个整数 left 和 right ,返回一个列表,列表的元素是范围 [left, right] 内所有的 自除数 。

 

示例 1:

输入:left = 1, right = 22
输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]
示例 2:

输入:left = 47, right = 85
输出:[48,55,66,77]
"""


class Solution:
    def __init__(self):
        self.left = 47
        self.right = 85

    def selfDividingNumbers(self):
        left = self.left
        right = self.right

        # 算法分析
        def chu(num):
            if num < 10:
                return True
            x = num
            while x:
                x, d = divmod(x, 10)
                if d == 0 or num % d:
                    return False
            return True

        return [i for i in range(left, right + 1) if chu(i)]

二倍数对数组

"""
给定一个长度为偶数的整数数组 arr,只有对 arr 进行重组后可以满足 “对于每个 0 <=i < len(arr) / 2,都有 arr[2 * i + 1] = 2 * arr[2 * i]”时,返回 true;否则,返回 false。

示例 1:

输入:arr = [3,1,3,6]
输出:false
示例 2:

输入:arr = [2,1,2,6]
输出:false
示例 3:

输入:arr = [4,-2,2,-4]
输出:true
解释:可以用 [-2,-4] 和 [2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]
"""
from collections import Counter


class Solution:
    def __init__(self):
        self.arr = [3, 1, 3, 6]

    def canReorderDoubled(self):
        arr = self.arr
        # 算法部分
        cnt = Counter(arr)
        if cnt[0] % 2:
            return False
        for x in sorted(cnt, key=abs):
            if cnt[2 * x] < cnt[x]:
                return False
            cnt[2 * x] -= cnt[x]
        return True

强密码检验器

"""
如果一个密码满足下述所有条件,则认为这个密码是强密码:
由至少 6 个,至多 20 个字符组成。
至少包含 一个小写 字母,一个大写 字母,和 一个数字 。
同一字符 不能 连续出现三次 (比如 "...aaa..." 是不允许的, 但是"...aa...a..." 如果满足其他条件也可以算是强密码)。
给你一个字符串 password ,返回将 password 修改到满足强密码条件需要的最少修改步数。如果 password 已经是强密码,则返回 0 。

在一步修改操作中,你可以:

插入一个字符到 password ,
从 password 中删除一个字符,或
用另一个字符来替换 password 中的某个字符。

示例 1:

输入:password = "a"
输出:5
示例 2:

输入:password = "aA1"
输出:3
示例 3:

输入:password = "1337C0d3"
输出:0
"""


class Solution:
    def __init__(self):
        self.password = "aA1"

    def strongPasswordChecker(self):
        password = self.password
        # 算法部分
        n = len(password)
        has_lower = has_upper = has_digit = False
        for ch in password:
            if ch.islower():
                has_lower = True
            elif ch.isupper():
                has_upper = True
            elif ch.isdigit():
                has_digit = True
        categories = has_lower + has_upper + has_digit

        if n < 6:
            return max(6 - n, 3 - categories)
        elif n <= 20:
            replace = cnt = 0
            cur = "#"

            for ch in password:
                if ch == cur:
                    cnt += 1
                else:
                    replace += cnt // 3
                    cnt = 1
                    cur = ch
            replace += cnt // 3
            return max(replace, 3 - categories)
        else:
            replace, remove = 0, n - 20
            rm2 = cnt = 0
            cur = "#"

            for ch in password:
                if ch == cur:
                    cnt += 1
                else:
                    if remove > 0 and cnt >= 3:
                        if cnt % 3 == 0:
                            remove -= 1
                            replace -= 1
                        elif cnt % 3 == 1:
                            rm2 += 1
                    replace += cnt // 3
                    cnt = 1
                    cur = ch

            if remove > 0 and cnt >= 3:
                if cnt % 3 == 0:
                    remove -= 1
                    replace -= 1
                elif cnt % 3 == 1:
                    rm2 += 1
            replace += cnt // 3

            use2 = min(replace, rm2, remove // 2)
            replace -= use2
            remove -= use2 * 2

            use3 = min(replace, remove // 3)
            replace -= use3
            remove -= use3 * 3
            return (n - 20) + max(replace, 3 - categories)

寻找比目标字母大的最小字母

class Solution:
    def nextGreatestLetter(self, letters: List[str], target: str) -> str:
     return letters[bisect_right(letters, target)] if target < letters[-1] else letters[0]

区域和检索 - 数组可修改

"""
给你一个数组 nums ,请你完成两类查询。

其中一类查询要求 更新 数组 nums 下标对应的值
另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 ,其中 left <= right
实现 NumArray 类:

NumArray(int[] nums) 用整数数组 nums 初始化对象
void update(int index, int val) 将 nums[index] 的值 更新 为 val
int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right])
 

示例 1:

输入:
["NumArray", "sumRange", "update", "sumRange"]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]

解释:
NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2);   // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8
"""


class NumArray:

    def __init__(self, nums):
        n = len(nums)
        size = int(n ** 0.5)
        sums = [0] * ((n + size - 1) // size)
        for i, num in enumerate(nums):
            sums[i // size] += num
        self.nums = nums
        self.sums = sums
        self.size = size

    def update(self, index, val) -> None:
        self.sums[index // self.size] += val - self.nums[index]
        self.nums[index] = val

    def sumRange(self, left, right):
        m = self.size
        b1, b2 = left // m, right // m
        if b1 == b2:
            return sum(self.nums[left:right + 1])
        return sum(self.nums[left:(b1 + 1) * m]) + sum(self.sums[b1 + 1:b2]) + sum(self.nums[b2 * m:right + 1])

# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# obj.update(index,val)
# param_2 = obj.sumRange(left,right)

二进制表示中质数个计算置位

class Solution:
    def countPrimeSetBits(self, left: int, right: int) -> int:
        return sum(((1 << x.bit_count()) & 665772) != 0 for x in range(left, right + 1))

最小高度树

"""
树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含n个节点的树,标记为0到n - 1 。给定数字n和一个有 n - 1 条无向边的 edges列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。

示例 1:


输入:n = 4, edges = [[1,0],[1,2],[1,3]]
输出:[1]
解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。
示例 2:


输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]
"""
from collections import deque


class Solution:
    def __init__(self):
        self.n = 6
        self.edges = [[3, 0], [3, 1], [3, 2], [3, 4], [5, 4]]

    def findMinHeightTrees(self):
        n = self.n
        edges = self.edges
        #算法部分
        if n == 1:
            return [0]

        g = [[] for _ in range(n)]
        for x, y in edges:
            g[x].append(y)
            g[y].append(x)
        parents = [0] * n

        def bfs(start: int):
            vis = [False] * n
            vis[start] = True
            q = deque([start])
            while q:
                x = q.popleft()
                for y in g[x]:
                    if not vis[y]:
                        vis[y] = True
                        parents[y] = x
                        q.append(y)
            return x

        x = bfs(0)  # 找到与节点 0 最远的节点 x
        y = bfs(x)  # 找到与节点 x 最远的节点 y

        path = []
        parents[x] = -1
        while y != -1:
            path.append(y)
            y = parents[y]
        m = len(path)
        return [path[m // 2]] if m % 2 else [path[m // 2 - 1], path[m // 2]]

旋转字符串

"""
给定两个字符串, s和goal。如果在若干次旋转操作之后,s能变成goal,那么返回true。

s的 旋转操作 就是将s 最左边的字符移动到最右边。

例如, 若s = 'abcde',在旋转一次之后结果就是'bcdea'。

示例 1:

输入: s = "abcde", goal = "cdeab"
输出: true
示例 2:

输入: s = "abcde", goal = "abced"
输出: false
"""


class Solution:
    def __init__(self):
        self.s = "abcde"
        self.goal = "abced"

    def rotateString(self):
        s = self.s
        goal = self.goal
        # 算法部分
        if len(s) != len(goal):
            return False
        for i in range(len(goal)):
            if goal[i] == s[0]:
                if (goal[i:]+goal[0:i])==s:
                    return True
        return False



if __name__ == '__main__':
    s = Solution()
    print(s.rotateString())

总结

这个系列暂时先到这里吧,看看以后会不会重启。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值