【Leetcode】精选算法top200道(二)

二、中等

339、嵌套列表权重和

给定一个嵌套的整数列表 nestedList ,每个元素要么是整数,要么是列表。同时,列表中元素同样也可以是整数或者是另一个列表。

整数的 深度 是其在列表内部的嵌套层数。例如,嵌套列表 [1,[2,2],[[3],2],1] 中每个整数的值就是其深度。

请返回该列表按深度加权后所有整数的总和。

链接:https://leetcode.cn/problems/nested-list-weight-sum
# """
# This is the interface that allows for creating nested lists.
# You should not implement it, or speculate about its implementation
# """
#class NestedInteger:
#    def __init__(self, value=None):
#        """
#        If value is not specified, initializes an empty list.
#        Otherwise initializes a single integer equal to value.
#        """
#
#    def isInteger(self):
#        """
#        @return True if this NestedInteger holds a single integer, rather than a nested list.
#        :rtype bool
#        """
#
#    def add(self, elem):
#        """
#        Set this NestedInteger to hold a nested list and adds a nested integer elem to it.
#        :rtype void
#        """
#
#    def setInteger(self, value):
#        """
#        Set this NestedInteger to hold a single integer equal to value.
#        :rtype void
#        """
#
#    def getInteger(self):
#        """
#        @return the single integer that this NestedInteger holds, if it holds a single integer
#        Return None if this NestedInteger holds a nested list
#        :rtype int
#        """
#
#    def getList(self):
#        """
#        @return the nested list that this NestedInteger holds, if it holds a nested list
#        Return None if this NestedInteger holds a single integer
#        :rtype List[NestedInteger]
#        """
class Solution:
    def depthSum(self, nestedList: List[NestedInteger]) -> int:
        dic1 = []
        dic2 = []
        def dfs(l, depth):
            for x in l:
                if x.isInteger():
                    dic1.append(x.getInteger())
                    dic2.append(depth)
                else:
                    dfs(x.getList(), depth+1)
        dfs(nestedList, 1)
        ans = 0
        for x, y in zip(dic1, dic2):
            ans += (x*y)
        return ans

348、设计井字棋

请在 n × n 的棋盘上,实现一个判定井字棋(Tic-Tac-Toe)胜负的神器,判断每一次玩家落子后,是否有胜出的玩家。

在这个井字棋游戏中,会有 2 名玩家,他们将轮流在棋盘上放置自己的棋子。

在实现这个判定器的过程中,你可以假设以下这些规则一定成立:

  1. 每一步棋都是在棋盘内的,并且只能被放置在一个空的格子里;

  2. 一旦游戏中有一名玩家胜出的话,游戏将不能再继续;

  3. 一个玩家如果在同一行、同一列或者同一斜对角线上都放置了自己的棋子,那么他便获得胜利。

链接:https://leetcode.cn/problems/design-tic-tac-toe
class TicTacToe:

    def __init__(self, n: int):
        # inf = 2*31 - 1
        self.keyboard = [['' for _ in range(n)] for _ in range(n)]
        self.n = n
        self.dic1_r = defaultdict(int)
        self.dic1_c = defaultdict(int)
        self.dic1_w = 0
        self.dic1_u = 0

        self.dic2_r = defaultdict(int)
        self.dic2_c = defaultdict(int)
        self.dic2_w = 0
        self.dic2_u = 0

    def move(self, row: int, col: int, player: int) -> int:
        if player == 1:
            self.keyboard[row][col] = 'X'
            self.dic1_r[row] += 1
            self.dic1_c[col] += 1
            if row == col:
                self.dic1_w+=1
            if row == self.n-col-1:
                self.dic1_u+=1
            
            if self.dic1_r[row] == self.n or self.dic1_c[col] == self.n:
                return 1
            if self.dic1_w==self.n or self.dic1_u == self.n:
                return 1
            return 0
        else:
            self.keyboard[row][col] = 'O'
            self.dic2_r[row] += 1
            self.dic2_c[col] += 1

            if row == col:
                self.dic2_w+=1
            if row == self.n-col-1:
                self.dic2_u+=1
            
            if self.dic2_r[row] == self.n or self.dic2_c[col] == self.n:
                return 2
            if self.dic2_w==self.n or self.dic2_u == self.n:
                return 2
            return 0
        return 0


# Your TicTacToe object will be instantiated and called as such:
# obj = TicTacToe(n)
# param_1 = obj.move(row,col,player)

360、有序转化数组

给你一个已经 排好序 的整数数组 nums 和整数 a 、 b 、 c 。对于数组中的每一个元素 nums[i] ,计算函数值 f(x) = ax2 + bx + c ,请 按升序返回数组 。

链接:https://leetcode.cn/problems/sort-transformed-array
class Solution:
    def sortTransformedArray(self, nums: List[int], a: int, b: int, c: int) -> List[int]:
        ans = []
        ### 一元一次方程
        if a == 0:
            for i,x in enumerate(nums):
                tmp = a*(x**2) + b*x + c
                ans.append(tmp)
            if b>0:
                return ans
            ### 常数
            elif b==0:
                return [c]*len(nums)
            else:
                return ans[::-1]
        l = -(b/(2*a))
        idx = -1
        minm = float("inf")
        for i,x in enumerate(nums):
            if abs(x-l) < minm:
                idx = i
                minm = abs(x-l)
        left, right = idx-1, idx+1
        while left >= 0 and right <= len(nums)-1:
            tmp = a*(nums[idx]**2) + b*nums[idx] + c
            ans.append(tmp)
            
            if abs(nums[left]-l) > abs(nums[right]-l):
                idx = right
                right += 1
            else:
                idx = left
                left -= 1
        tmp = a*(nums[idx]**2) + b*nums[idx] + c
        ans.append(tmp)
        if left != -1:
            for i in range(left,-1,-1):
                tmp = a*(nums[i]**2) + b*nums[i] + c
                ans.append(tmp)
        if right != len(nums) :
            for i in range(right, len(nums)):
                tmp = a*(nums[i]**2) + b*nums[i] + c
                ans.append(tmp)
        if a>0:
            return ans
        else:
            return ans[::-1]

362、敲击计数器

设计一个敲击计数器,使它可以统计在过去 5 分钟内被敲击次数。(即过去 300 秒)

您的系统应该接受一个时间戳参数 timestamp (单位为 秒 ),并且您可以假定对系统的调用是按时间顺序进行的(即 timestamp 是单调递增的)。几次撞击可能同时发生。

实现 HitCounter 类:

HitCounter() 初始化命中计数器系统。
void hit(int timestamp) 记录在 timestamp ( 单位为秒 )发生的一次命中。在同一个 timestamp 中可能会出现几个点击。
int getHits(int timestamp) 返回 timestamp 在过去 5 分钟内(即过去 300 秒)的命中次数。

链接:https://leetcode.cn/problems/design-hit-counter

class HitCounter:

    def __init__(self):
        self.times = defaultdict(int)
        self.time = []
        self.left = 0
        self.right = 0


    def hit(self, timestamp: int) -> None:
        if timestamp not in self.time:
            self.time.append(timestamp)
            while timestamp - self.time[self.left] >= 300:
                self.left += 1
        self.times[timestamp] += 1

        
    def getHits(self, timestamp: int) -> int:
        print(self.left, self.time)
        if timestamp not in self.time:
            while self.left<len(self.time) and timestamp - self.time[self.left] >= 300:
                self.left += 1
        ans = 0
        for i in range(self.left, len(self.time)):
            ans += self.times[self.time[i]]
        return ans


# Your HitCounter object will be instantiated and called as such:
# obj = HitCounter()
# obj.hit(timestamp)
# param_2 = obj.getHits(timestamp)

361、轰炸敌人

给你一个大小为 m x n 的矩阵 grid ,其中每个单元格都放置有一个字符:

'W' 表示一堵墙
'E' 表示一个敌人
'0'(数字 0)表示一个空位

返回你使用 一颗炸弹 可以击杀的最大敌人数目。你只能把炸弹放在一个空位里。

由于炸弹的威力不足以穿透墙体,炸弹只能击杀同一行和同一列没被墙体挡住的敌人。

链接:https://leetcode.cn/problems/bomb-enemy
class Solution:
    def maxKilledEnemies(self, grid: List[List[str]]) -> int:
        m, n = len(grid), len(grid[0])
        self.res = 0
        ### 超时
        # for i in range(m):
        #     for j in range(n):
        #         if grid[i][j] == '0':
        #             pathway = [(1,0),(0,1),(-1,0),(0,-1)]
        #             num = 0
        #             for k in range(4):
        #                 dr, dc = pathway[k]
        #                 nr, nc = i+dr, j+dc
        #                 while 0<=nr
        #                     if grid[nr][nc] == 'E':
        #                         num+=1
        #                     nr += dr
        #                     nc += dc
        #             self.res = max(self.res, num)
        # return self.res
        f_r = [[-1 for _ in range(n)] for _ in range(m)]
        f_c = [[-1 for _ in range(n)] for _ in range(m)]
        
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '0':
                    if i==0 or f_r[i-1][j]==-1:
                        pathway = [(1,0),(-1,0)]
                        num = 0
                        for k in range(2):
                            dr, dc = pathway[k]
                            nr, nc = i+dr, j+dc
                            while 0<=nr<m and 0<=nc<n and grid[nr][nc]!='W':
                                if grid[nr][nc] == 'E':
                                    num+=1
                                nr += dr
                                nc += dc
                        f_r[i][j] = num
                    else:
                        f_r[i][j] = f_r[i-1][j]
                    if j==0 or f_c[i][j-1] == -1:
                        pathway = [(0,1),(0,-1)]
                        num = 0
                        for k in range(2):
                            dr, dc = pathway[k]
                            nr, nc = i+dr, j+dc
                            while 0<=nr<m and 0<=nc<n and grid[nr][nc]!='W':
                                if grid[nr][nc] == 'E':
                                    num+=1
                                nr += dr
                                nc += dc
                        f_c[i][j] = num
                    else:
                        f_c[i][j] = f_c[i][j-1]
                    self.res = max(self.res, f_r[i][j]+f_c[i][j])
        return self.res

364、加权嵌套序列和二

给你一个整数嵌套列表 nestedList ,每一个元素要么是一个整数,要么是一个列表(这个列表中的每个元素也同样是整数或列表)。

整数的 深度 取决于它位于多少个列表内部。例如,嵌套列表 [1,[2,2],[[3],2],1] 的每个整数的值都等于它的 深度 。令 maxDepth 是任意整数的 最大深度 。
整数的 权重 为 maxDepth - (整数的深度) + 1 。
将 nestedList 列表中每个整数先乘权重再求和,返回该加权和。

链接:https://leetcode.cn/problems/nested-list-weight-sum-ii

# """
# This is the interface that allows for creating nested lists.
# You should not implement it, or speculate about its implementation
# """
#class NestedInteger:
#    def __init__(self, value=None):
#        """
#        If value is not specified, initializes an empty list.
#        Otherwise initializes a single integer equal to value.
#        """
#
#    def isInteger(self):
#        """
#        @return True if this NestedInteger holds a single integer, rather than a nested list.
#        :rtype bool
#        """
#
#    def add(self, elem):
#        """
#        Set this NestedInteger to hold a nested list and adds a nested integer elem to it.
#        :rtype void
#        """
#
#    def setInteger(self, value):
#        """
#        Set this NestedInteger to hold a single integer equal to value.
#        :rtype void
#        """
#
#    def getInteger(self):
#        """
#        @return the single integer that this NestedInteger holds, if it holds a single integer
#        Return None if this NestedInteger holds a nested list
#        :rtype int
#        """
#
#    def getList(self):
#        """
#        @return the nested list that this NestedInteger holds, if it holds a nested list
#        Return None if this NestedInteger holds a single integer
#        :rtype List[NestedInteger]
#        """
class Solution:
    def depthSumInverse(self, nestedList: List[NestedInteger]) -> int:
        self.depth = []
        self.nums = []
        def dfs(i, x, dist):
            l = x.getList()
            for j,y in enumerate(l):
                if y.isInteger():
                    self.depth.append(dist)
                    self.nums.append(y.getInteger())
                elif y.getList() == []:
                    self.depth.append(dist)
                    self.nums.append(0)
                else:
                    dfs(i+j, y, dist+1)
        for i,x in enumerate(nestedList):
            dp = 1
            if not x.isInteger():
                # if x.getList() != []:
                dfs(i, x, dp+1)
            else:
                self.depth.append(dp)
                self.nums.append(x.getInteger())
        if self.depth == []: return 0
        max_depth = max(self.depth)
        res = 0
        for i in range(len(self.depth)):
            res += (max_depth-self.depth[i]+1)*self.nums[i]
        print(self.depth, self.nums)
        return res

        

379、电话目录管理系统

设计一个电话目录管理系统,让它支持以下功能:

get: 分配给用户一个未被使用的电话号码,获取失败请返回 -1 check: 检查指定的电话号码是否被使用 release:
释放掉一个电话号码,使其能够重新被分配

链接:https://leetcode.cn/problems/design-phone-directory
class PhoneDirectory:

    def __init__(self, maxNumbers: int):
        self.dic = defaultdict(int)
        for i in range(maxNumbers):
            self.dic[i] = 0


    def get(self) -> int:
        for k, v in self.dic.items():
            if v == 0: 
                self.dic[k] = 1
                return k
        return -1

    def check(self, number: int) -> bool:
        if self.dic[number]==0:
            return True
        else:
            return False

    def release(self, number: int) -> None:
        self.dic[number] = 0


# Your PhoneDirectory object will be instantiated and called as such:
# obj = PhoneDirectory(maxNumbers)
# param_1 = obj.get()
# param_2 = obj.check(number)
# obj.release(number)

356、直线镜像

在一个二维平面空间中,给你 n 个点的坐标。问,是否能找出一条平行于 y 轴的直线,让这些点关于这条直线成镜像排布?

注意:题目数据中可能有重复的点。
class Solution:
    def isReflected(self, points: List[List[int]]) -> bool:
        def sorted_cmp(l1, l2):
            if l1[0] > l2[0]:
                return 1
            elif l1[0] == l2[0]:
                if l1[1] > l2[1]:
                    return 1
                else:
                    return -1
            else:
                return -1
        points.sort(key = cmp_to_key(sorted_cmp))
        arr = []
        for k in points:
            if k not in arr:
                arr.append(k)
        points = arr

        if len(points)%2 == 0:
            x = 0
            for l in points:
                x += l[0]
            x = x/len(points)
            i, j = 0, len(points)//2
            flag = True
            while i < (len(points)//2) and j < (len(points)):
                # print(i, j, points, x)
                if points[i][0] == points[j][0] == x:
                    flag = True
                elif points[i][1] != points[j][1] or x-points[i][0] != points[j][0]-x:
                    flag = False
                    break
                i+=1
                j+=1
            if flag:
                return True

            flag = True
            i, j = 0, len(points)-1
            while i < (len(points)//2) and j > (len(points)//2 -1):
                # print(flag, points, x)
                if points[i][0] == points[j][0] == x:
                    flag = True
                elif points[i][1] != points[j][1] or x-points[i][0] != points[j][0]-x:
                    flag = False
                    break
                i+=1
                j-=1
            if flag:
                return True
            return False
        else:
            x = points[len(points)//2][0]
            i, j = 0, len(points)//2+1
            flag = True
            while i < (len(points)//2) and j < (len(points)):
                if points[i][0] == points[j][0] == x:
                    flag = True
                elif points[i][1] != points[j][1] or x-points[i][0] != points[j][0]-x:
                    flag = False
                    break
                i+=1
                j+=1
            if flag:
                return True
            flag = True
            i, j = 0, len(points)-1
            while i < (len(points)//2) and j > (len(points)//2):
                if points[i][0] == points[j][0] == x:
                    flag = True
                elif points[i][1] != points[j][1] or x-points[i][0] != points[j][0]-x:
                    flag = False
                    break
                i+=1
                j-=1
            if flag:
                return True
            return False

351、安卓系统手势解锁

class Solution:
    def numberOfPatterns(self, m: int, n: int) -> int:
        dic = {
                1 : {3,7,9},
                2 : {8},
                3 : {1,9,7},
                4 : {6},
                5 : {},
                6 : {4},
                7 : {1, 9, 3},
                8 : {2},
                9 : {3, 7, 1}
        }
        self.res = 0
        visited = [False for _ in range(9)]
        def check(arr):
            if len(arr) == 1:
                return True
            for i in range(0,len(arr)-1):
                x = arr[i]
                y = arr[i+1]
                if y in dic[x]:
                    mid = (x + y)//2
                    if mid not in arr[:i]:
                        return False
            return True

        def backtrace(x, path):
            if x>=n+1:
                return
            if not check(path):
                return
            if x>=m and x<=n:
                self.res += 1
            for i in range(9):
                if not visited[i]:
                    visited[i] = True
                    backtrace(x+1, path+[i+1])
                    visited[i] = False
        for i in range(9):
            visited[i] = True
            backtrace(1, [i+1])
            visited[i] = False
        return self.res

549、二叉树中最长的连续序列

给定二叉树的根 root ,返回树中最长连续路径的长度。
连续路径是路径中相邻节点的值相差 1 的路径。此路径可以是增加或减少。

例如, [1,2,3,4] 和 [4,3,2,1] 都被认为有效,但路径 [1,2,4,3] 无效。
另一方面,路径可以是子-父-子顺序,不一定是父子顺序。

链接:https://leetcode.cn/problems/binary-tree-longest-consecutive-sequence-ii

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def longestConsecutive(self, root: Optional[TreeNode]) -> int:
        if not root: return 0
        self.res = 1
        
        def dfs(root):
            if not root: return (0, 0)
            left, right = dfs(root.left), dfs(root.right)
            inc = dec = 1
            if root.left:
                if root.left.val == root.val + 1:
                    inc += left[0]
                elif root.left.val == root.val - 1:
                    dec += left[1]
            if root.right:
                if root.right.val == root.val + 1:
                    inc = max(inc, right[0] + 1)
                elif root.right.val == root.val - 1:
                    dec = max(dec, right[1] + 1)
            
            self.res = max(self.res, inc + dec - 1)
            return (inc, dec)
        dfs(root)
        return self.res

510、二叉搜索树中的中序后继

给定一棵二叉搜索树和其中的一个节点 node ,找到该节点在树中的中序后继。如果节点没有中序后继,请返回 null 。

一个节点 node 的中序后继是键值比 node.val 大所有的节点中键值最小的那个。

你可以直接访问结点,但无法直接访问树。每个节点都会有其父节点的引用。节点 Node 定义如下:

class Node {
    public int val;
    public Node left;
    public Node right;
    public Node parent;
}

链接:https://leetcode.cn/problems/inorder-successor-in-bst-ii

"""
# Definition for a Node.
class Node:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        self.parent = None
"""

class Solution:
    def inorderSuccessor(self, node: 'Node') -> 'Optional[Node]':
        ###两种情况
        if node.right:
            node = node.right
            while node.left:
                node = node.left
            return node
        while node.parent and node == node.parent.right:
            node = node.parent
        return node.parent

487、最大连续1的个数二

给定一个二进制数组 nums ,如果最多可以翻转一个 0 ,则返回数组中连续 1 的最大个数。

class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        f = [[0,0] for _ in range(len(nums))]
        if nums[0] == 1:
            f[0][1] = 1
            f[0][0] = 0
        else:
            f[0][1] = 0
            f[0][0] = 1
        idx = 1
        res = 1
        while idx < len(nums):
            if nums[idx]==1:
                f[idx][0] = f[idx-1][0] + 1
                f[idx][1] = f[idx-1][1] + 1
            if nums[idx] == 0:
                f[idx][0] = f[idx-1][1] + 1
                f[idx][1] = 0
            res = max(res,f[idx][0],f[idx][1])
            idx += 1
        return res
        

439、三元表达式解析器

给定一个表示任意嵌套三元表达式的字符串 expression ,求值并返回其结果。

你可以总是假设给定的表达式是有效的,并且只包含数字, ‘?’ , ‘:’ , ‘T’ 和 ‘F’ ,其中 ‘T’ 为真, ‘F’ 为假。表达式中的所有数字都是 一位 数(即在 [0,9] 范围内)。

条件表达式从右到左分组(大多数语言中都是这样),表达式的结果总是为数字 ‘T’ 或 ‘F’ 。

链接:https://leetcode.cn/problems/ternary-expression-parser

class Solution:
    def parseTernary(self, expression: str) -> str:
        st = []
        is_flag = False
        for i in range(len(expression)-1, -1, -1):
            if expression[i] == ':':
                continue
            elif expression[i] == '?':
                is_flag = True
            else:
                if is_flag:
                    if expression[i] == 'T':
                        res = st[-1]
                        st.pop()
                        st.pop()
                        st.append(res)
                    else:
                        st.pop()
                    is_flag = False
                else:
                    st.append(expression[i])
        return st[0]

544、输出比赛匹配对

在 NBA 季后赛中,我们总是安排较强的队伍对战较弱的队伍,例如用排名第 1 的队伍和第 n 的队伍对决,这是一个可以让比赛更加有趣的好策略。现在,给你 n 支队伍,你需要以字符串格式输出它们的 最终 比赛配对。

n 支队伍按从 1 到 n 的正整数格式给出,分别代表它们的初始排名(排名 1 最强,排名 n 最弱)。我们用括号(‘(’, ‘)’)和逗号(‘,’)来表示匹配对——括号(‘(’, ‘)’)表示匹配,逗号(‘,’)来用于分割。 在每一轮的匹配过程中,你都需要遵循将强队与弱队配对的原则。

链接:https://leetcode.cn/problems/output-contest-matches

class Solution(object):
    def findContestMatch(self, n):
        team = [str(x) for x in range(1, n + 1)]
        while n > 1:
            for i in range(n // 2):
                team[i] = "({},{})".format(team[i], team[n-i-1])
            n //= 2

        return team[0]

484、寻找排列

由范围 [1,n] 内所有整数组成的 n 个整数的排列 perm 可以表示为长度为 n - 1 的字符串 s ,其中:

如果 perm[i] < perm[i + 1] ,那么 s[i] == ’ i ’
如果 perm[i] > perm[i + 1] ,那么 s[i] == ‘D’ 。
给定一个字符串 s ,重构字典序上最小的排列 perm 并返回它。

链接:https://leetcode.cn/problems/find-permutation

class Solution:
    def findPermutation(self, s: str) -> List[int]:
        ### DFS
        # n = len(s) + 1
        # self.res = []
        # visited = [False for _ in range(n)]
        # def dfs(arr, idx):
        #     if self.res != []:
        #         return
        #     if len(arr)==n:
        #         self.res = (arr)
        #         return
        #     if idx >= n-1:
        #         return
        #     for x in range(1,n+1):
        #         if s[idx] == 'I' and not visited[x-1] and x>(arr[-1]):
        #             visited[x-1] = True
        #             dfs(arr+[(x)], idx+1)
        #             visited[x-1] = False
        #         if s[idx] == 'D' and not visited[x-1] and x<(arr[-1]):
        #             visited[x-1] = True
        #             dfs(arr+[(x)], idx+1)
        #             visited[x-1] = False
        # for i in range(1, n+1):
        #     visited[i-1] = True
        #     dfs([(i)], 0)
        #     if self.res != []:
        #         break
        #     visited[i-1] = False
        # return (self.res)

        ### dp 不超时
        n = len(s) 
        nums = list(range(1, n+2))
        pre = s[0]
        cnt = 1
        for i in range(1, n):
            if pre == s[i]:
                cnt += 1
            else:
                if pre == 'D':
                    nums[i-cnt:i+1] = nums[i-cnt:i+1][::-1]
                pre = s[i]
                cnt = 1
        if pre == 'D':
            nums[n-cnt:n+1] = nums[n-cnt:n+1][::-1]
        return nums
        

555、分割连接字符串

给定一个字符串列表 strs,你可以将这些字符串连接成一个循环字符串,对于每个字符串,你可以选择是否翻转它。在所有可能的循环字符串中,你需要分割循环字符串(这将使循环字符串变成一个常规的字符串),然后找到字典序最大的字符串。

具体来说,要找到字典序最大的字符串,你需要经历两个阶段:

将所有字符串连接成一个循环字符串,你可以选择是否翻转某些字符串,并按照给定的顺序连接它们。
在循环字符串的某个位置分割它,这将使循环字符串从分割点变成一个常规的字符串。
你的工作是在所有可能的常规字符串中找到字典序最大的一个。

链接:https://leetcode.cn/problems/split-concatenated-strings

class Solution:
    def splitLoopedString(self, strs: List[str]) -> str:
        ### Limted time exceed
        # arr = []
        # res = ''
        # for s in strs:
        #     tmp = arr
        #     arr = []
        #     s1 = s
        #     s2 = s[::-1]
        #     if tmp == []:
        #         arr = [s1,s2]
        #     else:
        #         for x in tmp:
        #             arr.append(x+s1)
        #             arr.append(x+s2)
        # for x in arr:
        #     for i in range(len(x)):
        #         res = max(res, x[i:] + x[:i])
        # return (res)

        ####
        arr = [s if s>s[::-1] else s[::-1] for s in strs]
        res = ''.join(arr)
        for i in range(len(arr)):
            other =  ''.join(arr[i+1:]) + ''.join(arr[:i])
            for j in range(len(arr[i])):
                head = arr[i][:j]
                tail = arr[i][j:]
                cur = tail + other + head
                res = max(cur, res)
                cur = head[::-1] + other + tail[::-1]
                res = max(cur, res)
        return res

573、松鼠模拟

现在有一棵树,一只松鼠和一些坚果。位置由二维网格的单元格表示。你的目标是找到松鼠收集所有坚果的最小路程,且坚果是一颗接一颗地被放在树下。松鼠一次最多只能携带一颗坚果,松鼠可以向上,向下,向左和向右四个方向移动到相邻的单元格。移动次数表示路程。

链接:https://leetcode.cn/problems/squirrel-simulation

class Solution:
    def minDistance(self, height: int, width: int, tree: List[int], squirrel: List[int], nuts: List[List[int]]) -> int:
        path = float('inf')
        x, y = squirrel
        x_t, y_t = tree

        path_t = 0
        for i, j in nuts:
            cur = abs(i-x_t) + abs(j-y_t)
            path_t += cur*2

        for i,j in nuts:
            cur = path_t - (abs(i-x_t) + abs(j-y_t)) + (abs(i-x) + abs(j-y))
            path = min(path, cur)
        return path

634、寻找数组的错位排序

在组合数学中,如果一个排列中所有元素都不在原先的位置上,那么这个排列就被称为 错位排列 。

给定一个从 1 到 n 升序排列的数组,返回 不同的错位排列 的数量 。由于答案可能非常大,你只需要将答案对 109+7 取余 输出即可。

链接:https://leetcode.cn/problems/find-the-derangement-of-an-array

class Solution:
    def findDerangement(self, n: int) -> int:
        f = [0 for _ in range(n+1)]
        f[0] = 0
        f[1] = 1
        for i in range(2, n+1):
            f[i] = (i)*(f[i-1] + f[i-2]) % 1000000007
        return f[n-1]

1057、校园自行车分配

在 X-Y 平面上表示的校园中,有 n 名工人和 m 辆自行车,其中 n <= m。

给定一个长度为 n 的数组 workers ,其中 worker [i] = [xi, yi] 表示第 i 个工人的位置。你也得到一个长度为 m 的自行车数组 bikers ,其中 bikes[j] = [xj, yj] 是第 j 辆自行车的位置。所有给定的位置都是 唯一 的。

我们需要为每位工人分配一辆自行车。在所有可用的自行车和工人中,我们选取彼此之间 曼哈顿距离 最短的工人自行车对 (workeri, bikej) ,并将其中的自行车分配給工人。

如果有多个 (workeri, bikej) 对之间的 曼哈顿距离 相同,那么我们选择 工人索引最小 的那对。类似地,如果有多种不同的分配方法,则选择 自行车索引最小 的一对。不断重复这一过程,直到所有工人都分配到自行车为止。

返回长度为 n 的向量 answer,其中 answer[i] 是第 i 位工人分配到的自行车的索引(从 0 开始)。

给定两点 p1 和 p2 之间的 曼哈顿距离 为 Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|。

链接:https://leetcode.cn/problems/campus-bikes
class Solution:
    def assignBikes(self, workers: List[List[int]], bikes: List[List[int]]) -> List[int]:
        def Manhattan(p1, p2):
            return abs(p1[0]-p2[0])+abs(p1[1]-p2[1])
       
        from sortedcontainers import SortedDict as SD
        dic = SD()

        for i,b in enumerate(workers):
            for j,w in enumerate(bikes):
                dist = Manhattan(b, w)
                if dist not in dic:
                    dic[dist] = [(i,j)]
                else:
                    dic[dist].append((i, j))
        res = [0 for _ in range(len(workers))]
        visited_b = [False for _ in range(len(bikes))]
        visited_w = [False for _ in range(len(workers))]

        cnt = 0
        for k, v in dic.items():
            for x in v:
                i, j = x
                if not visited_w[i] and not visited_b[j]:
                    visited_w[i] = True
                    visited_b[j] = True
                    res[i] = j
                    cnt+=1
                    if cnt == len(workers):
                        break
        return res

505、迷宫二

由空地和墙组成的迷宫中有一个球。球可以向上下左右四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。

给定球的起始位置,目的地和迷宫,找出让球停在目的地的最短距离。距离的定义是球从起始位置(不包括)到目的地(包括)经过的空地个数。如果球无法停在目的地,返回 -1。

迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。

链接:https://leetcode.cn/problems/the-maze-ii

class Solution:
    def shortestDistance(self, maze: List[List[int]], start: List[int], destination: List[int]) -> int:
        queue = [(start[0], start[1])]
        m, n = len(maze), len(maze[0])
        dist = [[float('inf') for _ in range(n)]for _ in range(m)]
        dist[start[0]][start[1]] = 0
        while queue:
            cur_i, cur_j = queue.pop(0)
            print(cur_i, cur_j)
            pathway = [(1,0),(-1,0),(0,1),(0,-1)]
            for i in range(4):
                dr, dc = pathway[i]
                nr, nc = cur_i+dr, cur_j+dc
                step = 1
                while 0<=nr<m and 0<=nc<n and maze[nr][nc]==0:
                    nr += dr
                    nc += dc
                    step += 1
                nr -= dr
                nc -= dc
                step -= 1
                if dist[nr][nc]>dist[cur_i][cur_j]+step:
                    dist[nr][nc] = dist[cur_i][cur_j]+step
                    queue.append((nr, nc))
        if dist[destination[0]][destination[1]] == float('inf'):
            return -1
        else:
            return dist[destination[0]][destination[1]]

533、孤独像素二

给你一个大小为 m x n 的二维字符数组 picture ,表示一张黑白图像,数组中的 ‘B’ 表示黑色像素,‘W’ 表示白色像素。另给你一个整数 target ,请你找出并返回符合规则的 黑色 孤独像素的数量。

黑色孤独像素是指位于某一特定位置 (r, c) 的字符 ‘B’ ,其中:

行 r 和列 c 中的黑色像素恰好有 target 个。
列 c 中所有黑色像素所在的行必须和行 r 完全相同。

链接:https://leetcode.cn/problems/lonely-pixel-ii

class Solution:
    def findBlackPixel(self, picture: List[List[str]], target: int) -> int:
        m, n = len(picture), len(picture[0])
        dic_r = defaultdict(list)
        dic_c = defaultdict(list)

        for i in range(m):
            for j in range(n):
                if picture[i][j] == 'B':
                    dic_r[i].append(j)
                    dic_c[j].append(i)
        res = 0
        # print(dic_r, dic_c)
        for i in range(m):
            if len(dic_r[i])!=target:
                continue
            for j in range(n):
                if len(dic_c[j])!=target:
                    continue
                if picture[i][j]== 'B':
                    y = dic_r[i]
                    flag = True
                    for x in dic_c[j]:
                        if dic_r[x] != y:
                            flag = False
                            break
                    if flag: res+=1
        return res

635、设计日志存储系统

你将获得多条日志,每条日志都有唯一的 id 和 timestamp ,timestamp 是形如 Year:Month:Day:Hour:Minute:Second 的字符串,2017:01:01:23:59:59 ,所有值域都是零填充的十进制数。

实现 LogSystem 类:

LogSystem() 初始化 LogSystem 对象
void put(int id, string timestamp) 给定日志的 id 和 timestamp ,将这个日志存入你的存储系统中。
int[] retrieve(string start, string end, string granularity) 返回在给定时间区间 [start, end] (包含两端)内的所有日志的 id 。start 、end 和 timestamp 的格式相同,granularity 表示考虑的时间粒度(例如,精确到 Day、Minute 等)。例如 start = "2017:01:01:23:59:59"、end = "2017:01:02:23:59:59" 且 granularity = "Day" 意味着需要查找从 Jan. 1st 2017 到 Jan. 2nd 2017 范围内的日志,可以忽略日志的 Hour、Minute 和 Second 。

链接:https://leetcode.cn/problems/design-log-storage-system

class LogSystem:
    def __init__(self):
        self.dict = defaultdict(str)
        self.granularity_dict = {"Year": 4, "Month": 7, "Day": 10,
                                 "Hour": 13, "Minute": 16, "Second": 19}

    def put(self, id: int, timestamp: str) -> None:
        self.dict[id] = timestamp

    def retrieve(self, start: str, end: str, granularity: str) -> List[int]:
        check_len = self.granularity_dict[granularity]
        res = []
        for key, val in self.dict.items():
            if val[:check_len] >= start[:check_len] and val[:check_len] <= end[:check_len]:
                res.append(key)
        return res

545、二叉树的边界

二叉树的 边界 是由 根节点 、左边界 、按从左到右顺序的 叶节点 和 逆序的右边界 ,按顺序依次连接组成。

左边界 是满足下述定义的节点集合:

根节点的左子节点在左边界中。如果根节点不含左子节点,那么左边界就为 空 。
如果一个节点在左边界中,并且该节点有左子节点,那么它的左子节点也在左边界中。
如果一个节点在左边界中,并且该节点 不含 左子节点,那么它的右子节点就在左边界中。
最左侧的叶节点 不在 左边界中。
右边界 定义方式与 左边界 相同,只是将左替换成右。即,右边界是根节点右子树的右侧部分;叶节点 不是 右边界的组成部分;如果根节点不含右子节点,那么右边界为 空 。

叶节点 是没有任何子节点的节点。对于此问题,根节点 不是 叶节点。

给你一棵二叉树的根节点 root ,按顺序返回组成二叉树 边界 的这些值。

链接:https://leetcode.cn/problems/boundary-of-binary-tree

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def boundaryOfBinaryTree(self, root: Optional[TreeNode]) -> List[int]:
        if root and not root.left and not root.right:return [root.val]
        left = []
        bottom = []
        right = []
        def leftdfs(root):
            if root and (root.left or root.right):
                left.append(root.val)
                if root.left:
                    leftdfs(root.left)
                else:
                    leftdfs(root.right)
        def bottomdfs(root):
            if not root: return
            if not root.left and not root.right:
                bottom.append(root.val)
                return
            bottomdfs(root.left)
            bottomdfs(root.right)
        def rightdfs(root):
            if root and (root.left or root.right):
                right.append(root.val)
                if root.right:
                    rightdfs(root.right)
                else:
                    rightdfs(root.left)
        leftdfs(root.left)
        bottomdfs(root)
        rightdfs(root.right)

        return [root.val] + left + bottom + right[::-1]

616、给字符串添加加粗字符串

给你一个字符串 s 和一个字符串列表 words ,你需要将在字符串列表中出现过的 s 的子串添加加粗闭合标签

如果两个子串有重叠部分,你需要把它们一起用一对闭合标签包围起来。同理,如果两个子字符串连续被加粗,那么你也需要把它们合起来用一对加粗标签包围。

返回添加加粗标签后的字符串 s 。

链接:https://leetcode.cn/problems/add-bold-tag-in-string

class Solution:
    def addBoldTag(self, s: str, words: List[str]) -> str:
        n = len(s)
        mask = [False for _ in range(n)]
        for i in range(n):
            for word in words:
                if i+len(word)<=n and s[i:i+len(word)]==word:
                    for j in range(i, i+len(word)):
                        mask[j] = True
        res = ''
        for i in range(n):
            if mask[i] and (i==0 or mask[i-1]==False):
                res+=''
            res+=s[i]
            if mask[i] and (i==n-1 or mask[i+1]==False):
                res+=''
        return res

624、数组列表中的最大距离

给定 m 个数组,每个数组都已经按照升序排好序了。现在你需要从两个不同的数组中选择两个整数(每个数组选一个)并且计算它们的距离。两个整数 a 和 b 之间的距离定义为它们差的绝对值 |a-b| 。你的任务就是去找到最大距离

链接:https://leetcode.cn/problems/maximum-distance-in-arrays
class Solution:
    def maxDistance(self, arrays: List[List[int]]) -> int:
        m = len(arrays)
        res = -float('inf')
        arr = []
        for i in range(m):
            if len(arrays[i]) == 1 or arrays[i][0]==arrays[i][-1]:
                arr.append((arrays[i][0], i))
            else:
                minn, maxn = arrays[i][0], arrays[i][-1]
                arr.append((minn, i))
                arr.append((maxn, i))
        arr.sort(key = lambda x:x[0])
        print(arr)
        if arr[0][1] == arr[-1][1]:
            res = max(abs(arr[-1][0]-arr[1][0]), abs(arr[-2][0]-arr[0][0]))
        else:
            res = arr[-1][0]-arr[0][0]
        return res

723、粉碎糖果

这个问题是实现一个简单的消除算法。

给定一个 m x n 的二维整数数组 board 代表糖果所在的方格,不同的正整数 board[i][j] 代表不同种类的糖果,如果 board[i][j] == 0 代表 (i, j) 这个位置是空的。

给定的方格是玩家移动后的游戏状态,现在需要你根据以下规则粉碎糖果,使得整个方格处于稳定状态并最终输出:

如果有三个及以上水平或者垂直相连的同种糖果,同一时间将它们粉碎,即将这些位置变成空的。
在同时粉碎掉这些糖果之后,如果有一个空的位置上方还有糖果,那么上方的糖果就会下落直到碰到下方的糖果或者底部,这些糖果都是同时下落,也不会有新的糖果从顶部出现并落下来。
通过前两步的操作,可能又会出现可以粉碎的糖果,请继续重复前面的操作。
当不存在可以粉碎的糖果,也就是状态稳定之后,请输出最终的状态。
你需要模拟上述规则并使整个方格达到稳定状态,并输出。

链接:https://leetcode.cn/problems/candy-crush

class Solution:
    def candyCrush(self, board: List[List[int]]) -> List[List[int]]:
        m, n = len(board), len(board[0])
        mask = [[False for _ in range(n)]for _ in range(m)]
        while 1:
            mask = [[False for _ in range(n)]for _ in range(m)]
            flag = False
            for i in range(m):
                for j in range(n-2):
                    if board[i][j]!=0 and board[i][j] == board[i][j+1] == board[i][j+2]:
                        flag = True
                        mask[i][j] = mask[i][j+1] = mask[i][j+2] = True
            for i in range(n):
                for j in range(m-2):
                    if board[j][i]!=0 and board[j][i] == board[j+1][i] == board[j+2][i]:
                        flag = True
                        mask[j][i] = mask[j+1][i] = mask[j+2][i] = True

            if flag:
                for c in range(n):
                    rr = m - 1
                    for r in range(m - 1, -1, -1):
                        if mask[r][c] == False:
                            board[rr][c] = board[r][c]
                            rr -= 1
                    while rr > -1:
                        board[rr][c] = 0
                        rr -= 1
            else:
                break
        return board

737、句子相似性二

并查集
我们可以将一个句子表示为一个单词数组,例如,句子 I am happy with leetcode"可以表示为 arr = [“I”,“am”,happy",“with”,“leetcode”]

给定两个句子 sentence1 和 sentence2 分别表示为一个字符串数组,并给定一个字符串对 similarPairs ,其中 similarPairs[i] = [xi, yi] 表示两个单词 xi 和 yi 是相似的。

如果 sentence1 和 sentence2 相似则返回 true ,如果不相似则返回 false 。

两个句子是相似的,如果:

  • 它们具有 相同的长度 (即相同的字数)
  • sentence1[i] 和 sentence2[i] 是相似的

请注意,一个词总是与它自己相似,也请注意,相似关系是可传递的。例如,如果单词 a 和 b 是相似的,单词 b 和 c 也是相似的,那么 a 和 c 也是 相似 的。

链接:https://leetcode.cn/problems/sentence-similarity-ii

class Union:
    def __init__(self, n):
        self.father = [i for i in range(n)]
    def findFather(self, x):
        while x != self.father[x]:
            self.father[x] = self.father[self.father[x]]
            x = self.father[x]
        return x
    def union(self, x, y):
        fa_x = self.findFather(x)
        fa_y = self.findFather(y)
        if fa_x != fa_y:
            if fa_x < fa_y:
                fa_x, fa_y = fa_y, fa_x
            self.father[fa_y] = fa_x
    def isConnect(self, x, y):
        fa_x = self.findFather(x)
        fa_y = self.findFather(y)
        return fa_x==fa_y
class Solution:
    def areSentencesSimilarTwo(self, sentence1: List[str], sentence2: List[str], similarPairs: List[List[str]]) -> bool:
        
        if len(sentence1) != len(sentence2): return False
        n = len(sentence1)
        
        sentence = sentence1 + sentence2
        dic = dict()
        Id = 0
        for x in sentence:
            if x not in dic:
                dic[x] = Id
                Id += 1
        for x,y in similarPairs:
            if x not in dic:
                dic[x] = Id
                Id += 1
            if y not in dic:
                dic[y] = Id
                Id += 1
        uf = Union(Id)
        for x,y in similarPairs:
            uf.union(dic[x], dic[y])
        for i in range(n):
            if not uf.isConnect(dic[sentence1[i]], dic[sentence2[i]]):
                return False
        return True

651、四键键盘

假设你有一个特殊的键盘包含下面的按键:

A:在屏幕上打印一个 'A'。
Ctrl-A:选中整个屏幕。
Ctrl-C:复制选中区域到缓冲区。
Ctrl-V:将缓冲区内容输出到上次输入的结束位置,并显示在屏幕上。
现在,你可以 最多 按键 n 次(使用上述四种按键),返回屏幕上最多可以显示 'A' 的个数 。

链接:https://leetcode.cn/problems/4-keys-keyboard

class Solution:
    def maxA(self, n: int) -> int:
        #### dfs
        # self.res = 0
        # def dfs(cur_i, nums, tmp):
        #     if cur_i == n:
        #         self.res = max(self.res, nums)
        #         return 
        #     pathway = [1,3,1]
        #     for i in range(3):
        #         di = pathway[i]
        #         if 0 <= cur_i+di <= n:
        #             ni = cur_i + di
        #             if i == 0:
        #                 dfs(ni, nums+1, tmp)
        #             elif i==1:
        #                 dfs(ni, nums*2, nums)
        #             elif i==2:
        #                 dfs(ni, nums+tmp, tmp)
                
        # dfs(0,0,0)
        # return self.res

        #### dp
        self.res = 0
        f = [0 for _ in range(n)]
        f[0] = 1

        for i in range(1, n):
            f[i] = f[i-1] + 1
            for j in range(i):
                f[i] = max(f[i], f[j]*(i-j-2+1))
        return (f[n-1])

663、均匀树划分

给定一棵有 n 个结点的二叉树,你的任务是检查是否可以通过去掉树上的一条边将树分成两棵,且这两棵树结点之和相等。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def checkEqualTree(self, root: Optional[TreeNode]) -> bool:
        def sumTree(root):
            if not root:
                return 0
            left = sumTree(root.left)
            right = sumTree(root.right)
            return root.val + left + right
        summ = sumTree(root)
        if summ % 2 != 0:
            return False
        target = summ//2
        def findtarget(root, pre):
            if not root:
                return False
            if pre and sumTree(root) == target:
                return True
            return findtarget(root.left, root) or findtarget(root.right, root)
        return findtarget(root, None)

666、路经总和四

对于一棵深度小于 5 的树,可以用一组三位十进制整数来表示。对于每个整数:

百位上的数字表示这个节点的深度 d,1 <= d <= 4。
十位上的数字表示这个节点在当前层所在的位置 P, 1 <= p <= 8。位置编号与一棵满二叉树的位置编号相同。
个位上的数字表示这个节点的权值 v,0 <= v <= 9。
给定一个包含三位整数的 升序 数组 nums ,表示一棵深度小于 5 的二叉树,请你返回 从根到所有叶子结点的路径之和 。

保证 给定的数组表示一个有效的连接二叉树。

链接:https://leetcode.cn/problems/path-sum-iv

class Solution:
    def pathSum(self, nums: List[int]) -> int:
        def int2arr(x):
            arr = [int(str(x)[0]),int(str(x)[1]) ,int(str(x)[2])]
            return arr
        arr = []
        for x in nums:
            tmp = int2arr(x)
            arr.append(tmp)
        root = arr[0]
        st = [root]
        res = []
        while st:
            tmp = st
            st = []
            while tmp:
                depth, width, summ = tmp.pop(0)
                flag1, flag2=False, False
                for d, w, n in arr:
                    if d==depth+1 and w == width*2-1:
                        st.append([d, w, summ+n])
                        flag1=True
                for d, w, n in arr:
                    if d==depth+1 and w == width*2:
                        st.append([d, w, summ+n])
                        flag1=True
                if not flag1 and not flag2:
                    res.append(summ)
        return (sum(res))

1066、校园自行车

1120、子树的平均值

给你一棵二叉树的根节点 root,找出这棵树的 每一棵 子树的 平均值 中的 最大 值。
子树是树中的任意节点和它的所有后代构成的集合。
树的平均值是树中节点值的总和除以节点数。

链接:https://leetcode.cn/problems/maximum-average-subtree

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maximumAverageSubtree(self, root: Optional[TreeNode]) -> float:
        def sumTree(root, nums):
            if not root:
                return 0, 0
            left, nums_l = sumTree(root.left, nums+1)
            right, nums_r = sumTree(root.right, nums+1)
            return root.val + left + right, nums_l+nums_r+1
        self.res = 0
        def dfs(root):
            if not root:
                return
            dfs(root.left)
            dfs(root.right)
            summ, nums = sumTree(root, 0)
            self.res = max(self.res, summ/nums)
        dfs(root)
        return self.res

1152、用户网站访问行为分析

给定两个字符串数组 username 和 website 和一个整数数组 timestamp 。给定的数组长度相同,其中元组 [username[i], website[i], timestamp[i]] 表示用户 username[i] 在时间 timestamp[i] 访问了网站 website[i] 。

访问模式 是包含三个网站的列表(不一定是完全不同的)。

例如,[“home”, “away”, “love”], [“leetcode”, “love”, “leetcode”],和 [“luffy”, “luffy”, “luffy”] 都是模式。
一种 访问模式 的 得分 是访问该模式中所有网站的用户数量,这些网站在该模式中出现的顺序相同。

例如,如果模式是 [“home”,“away”,“love”] ,那么分数就是用户数量 x , x 访问了 “home” ,然后访问了 “away” ,然后访问了 “love” 。
同样,如果模式是 [“leetcode”, “love”, “leetcode”] ,那么分数就是用户数量 x ,使得 x 访问了"leetcode",然后访问了 “love” ,之后又访问了 “leetcode” 。
另外,如果模式是 [“luffy”,“luffy”,“luffy”] ,那么分数就是用户数量 x ,这样 x 就可以在不同的时间戳上访问 “luffy” 三次。
返回 得分 最大的 访问模式 。如果有多个访问模式具有相同的最大分数,则返回字典序最小的。

链接:https://leetcode.cn/problems/analyze-user-website-visit-pattern

class Solution:
    def mostVisitedPattern(self, username: List[str], timestamp: List[int], website: List[str]) -> List[str]:
        dict_input = dict(zip(timestamp, zip(username, website)))
        dict_user = defaultdict(list)
        from sortedcontainers import SortedDict as SD
        mode = SD()
        print(sorted(dict_input.keys()))
        # for i in range(len(username)):
        for key in sorted(dict_input.keys()):
            user, web = dict_input[key]
            dict_user[user].append(web)
        for k,v in dict_user.items():
            if len(v)>=3:
                tmp_path_set = set() 
                for i in range(len(v)-2):
                    for j in range(i+1, len(v)-1):
                        for z in range(j+1, len(v)):
                            tmp_path_set.add(tuple([v[i],v[j],v[z]]))
                for p in tmp_path_set:
                    if p not in mode:
                        mode[p] = 0
                    mode[p] += 1
        print(dict_user, mode)
        maxm = 0
        self.res = []
        for k,v in mode.items():
            if v>maxm:
                maxm = v
                self.res = list(k)
        return (self.res)

1166、设计文件系统

你需要设计一个文件系统,你可以创建新的路径并将它们与不同的值关联。

路径的格式是一个或多个连接在一起的字符串,形式为: / ,后面跟着一个或多个小写英文字母。例如, " /leetcode" 和 “/leetcode/problems” 是有效路径,而空字符串 “” 和 “/” 不是。

实现 FileSystem 类:

bool createPath(string path, int value) 创建一个新的 path ,并在可能的情况下关联一个 value ,然后返回 true 。如果路径已经存在或其父路径不存在,则返回 false 。
int get(string path) 返回与 path 关联的值,如果路径不存在则返回 -1 。

链接:https://leetcode.cn/problems/design-file-system

class FileSystem:

    def __init__(self):
        self.path_dict = defaultdict(int)

    def createPath(self, path: str, value: int) -> bool:
        if path == '' or path == '/':
            return False
        arr = path.split('/')[1:]
        if '/' in arr:
            return False
        if tuple(arr) in self.path_dict:
            return False
        elif len(arr)>1:
            if tuple(arr[:len(arr)-1]) not in self.path_dict:
                return False
        self.path_dict[tuple(arr)] = value
        return True

    def get(self, path: str) -> int:
        arr = path.split('/')[1:]
        if tuple(arr) not in self.path_dict:
            return -1
        else:
            return self.path_dict[tuple(arr)]


# Your FileSystem object will be instantiated and called as such:
# obj = FileSystem()
# param_1 = obj.createPath(path,value)
# param_2 = obj.get(path)

1167、连接棒材的最低费用

你有一些长度为正整数的棍子。这些长度以数组 sticks 的形式给出, sticks[i] 是 第i个 木棍的长度。

你可以通过支付 x + y 的成本将任意两个长度为 x 和 y 的棍子连接成一个棍子。你必须连接所有的棍子,直到剩下一个棍子。

返回以这种方式将所有给定的棍子连接成一个棍子的 最小成本 。

链接:https://leetcode.cn/problems/minimum-cost-to-connect-sticks

用heapq排序,每次取最小的两个,用sort会超时

class Solution:
    def connectSticks(self, sticks: List[int]) -> int:
		ans = 0
        heap = []
        for n in sticks:
            heapq.heappush(heap, n)
        while len(heap) >= 2:
            a = heapq.heappop(heap)
            b = heapq.heappop(heap)
            c = a + b
            ans += c
            heapq.heappush(heap, c)
        return ans

1198、找出所有行的最小公共元素

利用set的特点

class Solution:
    def smallestCommonElement(self, mat: List[List[int]]) -> int:
        a = set(mat[0])
        for i in range(len(mat)):
            a = a & set(mat[i]) 
            print(a)
        a = list(a)
        if len(a) == 0:
            return -1
        return a[0]

1214、查找两颗二叉树之和

给出两棵二叉搜索树的根节点 root1 和 root2 ,请你从两棵树中各找出一个节点,使得这两个节点的值之和等于目标值 Target。

如果可以找到返回 True,否则返回 False。

链接:https://leetcode.cn/problems/two-sum-bsts

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def twoSumBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool:
        self.res = []
        def dfs(root):
            if not root: return
            dfs(root.left)
            self.res.append(root.val)
            dfs(root.right)
        dfs(root1)
        res1 = self.res.copy()
        self.res = []
        dfs(root2)
        res2 = self.res.copy()
        for x in res1:
            if target - x in res2:
                return True
        return False

1244、力扣排行榜

新一轮的「力扣杯」编程大赛即将启动,为了动态显示参赛者的得分数据,需要设计一个排行榜 Leaderboard。

请你帮忙来设计这个 Leaderboard 类,使得它有如下 3 个函数:

addScore(playerId, score):
假如参赛者已经在排行榜上,就给他的当前得分增加 score 点分值并更新排行。
假如该参赛者不在排行榜上,就把他添加到榜单上,并且将分数设置为 score。
top(K):返回前 K 名参赛者的 得分总和。
reset(playerId):将指定参赛者的成绩清零(换句话说,将其从排行榜中删除)。题目保证在调用此函数前,该参赛者已有成绩,并且在榜单上。
请注意,在初始状态下,排行榜是空的。

链接:https://leetcode.cn/problems/design-a-leaderboard

class Leaderboard:

    def __init__(self):
        self.board = dict()

    def addScore(self, playerId: int, score: int) -> None:
        if playerId not in self.board:
            self.board[playerId] = score
        else:
            self.board[playerId] += score

    def top(self, K: int) -> int:
        a = sorted(self.board.items(), key=lambda x:x[1], reverse=True)
        ans = 0
        a = a[:K]
        for k, v in a:
            ans += v
        return ans

    def reset(self, playerId: int) -> None:
        if playerId in self.board:
            del self.board[playerId]


# Your Leaderboard object will be instantiated and called as such:
# obj = Leaderboard()
# obj.addScore(playerId,score)
# param_2 = obj.top(K)
# obj.reset(playerId)

1265、逆序打印不可变链表

给您一个不可变的链表,使用下列接口逆序打印每个节点的值:

ImmutableListNode: 描述不可变链表的接口,链表的头节点已给出。
您需要使用以下函数来访问此链表(您 不能 直接访问 ImmutableListNode):

ImmutableListNode.printValue():打印当前节点的值。
ImmutableListNode.getNext():返回下一个节点。
输入只用来内部初始化链表。您不可以通过修改链表解决问题。也就是说,您只能通过上述 API 来操作链表。

链接:https://leetcode.cn/problems/print-immutable-linked-list-in-reverse

# """
# This is the ImmutableListNode's API interface.
# You should not implement it, or speculate about its implementation.
# """
# class ImmutableListNode:
#     def printValue(self) -> None: # print the value of this node.
#     def getNext(self) -> 'ImmutableListNode': # return the next node.

class Solution:
    def printLinkedListInReverse(self, head: 'ImmutableListNode') -> None:
        
        def dfs(node):
            if node == None: return
            dfs(node.getNext())
            node.printValue()
        dfs(head)
        

1215、进步数

如果一个整数上的每一位数字与其相邻位上的数字的绝对差都是 1,那么这个数就是一个「步进数」。

例如,321 是一个步进数,而 421 不是。

给你两个整数,low 和 high,请你找出在 [low, high] 范围内的所有步进数,并返回 排序后 的结果。

链接:https://leetcode.cn/problems/stepping-numbers

BFS

class Solution:
    def countSteppingNumbers(self, low: int, high: int) -> List[int]:
        res = []
        if low == 0:
            res.append(low)
        queue = [x for x in range(1,10)]
        while queue:
            tmp = queue.pop(0)
            if tmp > high:
                return res
            if tmp >= low:
                res.append(tmp)
            last = tmp % 10
            if last > 0:
                queue.append(tmp*10 + last - 1)
            if last < 9:
                queue.append(tmp*10 + last + 1)
        return res

1256、加密数字

给你一个非负整数 num ,返回它的「加密字符串」。

加密的过程是把一个整数用某个未知函数进行转化,你需要从下表推测出该转化函数:

class Solution:
    def encode(self, num: int) -> str:
        new_num = bin(num+1)
        print(new_num)
        length = len(str(new_num))
        return str(new_num)[3: length]

1182、与目标颜色间的最短距离

给你一个数组 colors,里面有 1、2、 3 三种颜色。

我们需要在 colors 上进行一些查询操作 queries,其中每个待查项都由两个整数 i 和 c 组成。
现在请你帮忙设计一个算法,查找从索引 i 到具有目标颜色 c 的元素之间的最短距离。
如果不存在解决方案,请返回 -1。

链接:https://leetcode.cn/problems/shortest-distance-to-target-color

class Solution:
    def shortestDistanceColor(self, colors: List[int], queries: List[List[int]]) -> List[int]:
        ans = []
        dic_color = defaultdict(list)
        for idx,x in enumerate(colors):
            dic_color[x].append(idx)
        for x,y in queries:
            if y not in dic_color:
                ans.append(-1)
            else:
                if colors[x] == y:
                    ans.append(0)
                else:
                    arr = dic_color[y]
                    left, right = 0, len(arr)-1
                    if x<=arr[left]:
                        ans.append(abs(arr[left]-x))
                    elif x>=arr[right]:
                        ans.append(abs(arr[right]-x))
                    else:
                        while left <= right:
                            mid = (left+right)//2
                            if x > arr[mid]: left = mid+1
                            else: right = mid-1
                        ans.append(min(abs(arr[left] - x), abs(arr[left - 1] - x)))
        return ans

1230、抛掷硬币

有一些不规则的硬币。在这些硬币中,prob[i] 表示第 i 枚硬币正面朝上的概率。

请对每一枚硬币抛掷 一次,然后返回正面朝上的硬币数等于 target 的概率。

链接:https://leetcode.cn/problems/toss-strange-coins

class Solution:
    def probabilityOfHeads(self, prob: List[float], target: int) -> float:
        m = len(prob)
        dp = [[0 for _ in range(target+1)] for _ in range(m+1)]
        dp[0][0] = 1
        for i in range(m):
            dp[i+1][0] = dp[i][0]*(1-prob[i])
        for i in range(1,m+1):
            for j in range(1,target+1):
                dp[i][j] = dp[i-1][j]*(1-prob[i-1])+dp[i-1][j-1]*prob[i-1]
        return dp[-1][-1]

1258、近义词替换

给你一个近义词表 synonyms 和一个句子 text , synonyms 表中是一些近义词对 ,你可以将句子 text 中每个单词用它的近义词来替换。

请你找出所有用近义词替换后的句子,按 字典序排序 后返回。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/synonymous-sentences

PS:利用并查集,确定近义词集合

class Union:
    def __init__(self, n):
        self.father = [x for x in range(n)]
    def findFather(self, x):
        while x!=self.father[x]:
            self.father[x] = self.father[self.father[x]]
            x = self.father[x]
        return x
    def union(self,x,y):
        root1 = self.findFather(x)
        root2 = self.findFather(y)
        if root1 != root2:
            if root2 > root1:
                self.father[root2] = root1
            else:
                self.father[root1] = root2
    def isConnect(self,x,y):
        return self.findFather(x)==self.findFather(y)

class Solution:
    def generateSentences(self, synonyms: List[List[str]], text: str) -> List[str]:
        arr_str = text.split(' ')
        word_id_dict = defaultdict(int)
        idx = 0
        for x,y in synonyms:
            if x not in word_id_dict:
                word_id_dict[x] = idx
                idx+=1
            if y not in word_id_dict:
                word_id_dict[y] = idx
                idx+=1
        uf = Union(idx)
        for x, y in synonyms:
            uf.union(word_id_dict[x], word_id_dict[y])

        syn_dict = defaultdict(set)
        for x in word_id_dict.keys():
            for y in word_id_dict.keys():
                if x==y:
                    continue
                if uf.isConnect(word_id_dict[x], word_id_dict[y]):
                    syn_dict[x].add(y)
        self.res = set()
        self.res.add(text)
        def dfs(arr, i):
            if i == len(arr):
                return
            for j in range(i,len(arr)):
                print(arr[j])
                if arr[j] in syn_dict:
                    for v in syn_dict[arr[j]]:
                        tmp = arr.copy()
                        tmp[j] = v
                        self.res.add(' '.join(tmp))
                        dfs(tmp, j+1)
                        tmp = arr.copy()
                        self.res.add(' '.join(tmp))
                        dfs(tmp, j+1)
        dfs(arr_str,0)
        self.res = list(self.res)
        self.res.sort()
        return self.res


1236、网络爬虫

给定一个链接 startUrl 和一个接口 HtmlParser ,请你实现一个网络爬虫,以实现爬取同 startUrl 拥有相同 域名标签 的全部链接。该爬虫得到的全部链接可以 任何顺序 返回结果。

链接:https://leetcode.cn/problems/web-crawler

# """
# This is HtmlParser's API interface.
# You should not implement it, or speculate about its implementation
# """
#class HtmlParser(object):
#    def getUrls(self, url):
#        """
#        :type url: str
#        :rtype List[str]
#        """

class Solution:
    def crawl(self, startUrl: str, htmlParser: 'HtmlParser') -> List[str]:
        start_strr=startUrl[7:]
        start_name = 'http://'+start_strr.split('/')[0]
        res = set()
        res.add(startUrl)
        st = [startUrl]
        while st:
            tmp_str = st.pop(0)
            res.add(tmp_str)
            for x in htmlParser.getUrls(tmp_str):
                if x not in res and x.startswith(start_name):
                    st.append(x)
        return (res)

1257、最小化公共区域

给你一些区域列表 regions ,每个列表的第一个区域都包含这个列表内所有其他区域。

很自然地,如果区域 X 包含区域 Y ,那么区域 X 比区域 Y 大。
给定两个区域 region1 和 region2 ,找到同时包含这两个区域的 最小 区域。
如果区域列表中 r1 包含 r2 和 r3 ,那么数据保证 r2 不会包含 r3 。
数据同样保证最小公共区域一定存在。

链接:https://leetcode.cn/problems/smallest-common-region

class Solution:
    def findSmallestRegion(self, regions: List[List[str]], region1: str, region2: str) -> str:
        fa_dict = dict()
        for region in regions:
            fa = region[0]
            for child in region[1:]:
                fa_dict[child] = fa
        r1, r2 = region1, region2
        r1_fas = [r1]
        while r1 in fa_dict:
            r1 = fa_dict[r1]
            r1_fas.append(r1)
        while r2 in fa_dict:
            if r2 in r1_fas:
                return r2
            r2 = fa_dict[r2]
        return r2

1272、删除区间

实数集合可以表示为若干不相交区间的并集,其中每个区间的形式为 [a, b)(左闭右开),表示满足 a <= x < b 的所有实数 x 的集合。如果某个区间 [a, b) 中包含实数 x ,则称实数 x 在集合中。

给你一个 有序的 不相交区间列表 intervals 。intervals 表示一个实数集合,其中每一项 intervals[i] = [ai, bi] 都表示一个区间 [ai, bi) 。再给你一个要删除的区间 toBeRemoved 。

返回 一组实数,该实数表示intervals 中 删除 了 toBeRemoved 的部分 。换句话说,返回实数集合,并满足集合中的每个实数 x 都在 intervals 中,但不在 toBeRemoved 中。你的答案应该是一个如上所述的 有序的 不相连的间隔列表 。

链接:https://leetcode.cn/problems/remove-interval

class Solution:
    def removeInterval(self, intervals: List[List[int]], toBeRemoved: List[int]) -> List[List[int]]:
        res = []
        for x,y in intervals:
            tmp_x = min(x, toBeRemoved[0])
            tmp_y = max(y, toBeRemoved[1])
            if tmp_x == x and tmp_y == y:
                if tmp_x == x == toBeRemoved[0] and tmp_y == y == toBeRemoved[1]:
                    continue
                elif tmp_x == x == toBeRemoved[0]:
                    res.append([toBeRemoved[1], y])
                elif tmp_y == y == toBeRemoved[1]:
                    res.append([x, toBeRemoved[0]])
                else:
                    res.append([x,toBeRemoved[0]])
                    res.append([toBeRemoved[1],y])
                
            elif tmp_x == x and tmp_y == toBeRemoved[1]:
                res.append([x, min(y,toBeRemoved[0])])
            elif tmp_x == toBeRemoved[0] and tmp_y == y:
                res.append([max(x,toBeRemoved[1]), y])
        return res

1273、删除树节点

class Solution:
    def deleteTreeNodes(self, nodes: int, parent: List[int], value: List[int]) -> int:
        edges = [[] for _ in range(nodes)]
        for x, p in enumerate(parent):
            if p != -1:
                edges[p].append(x)
        cnt_node = [1 for _ in range(nodes)]
        def dfs(node):
            for v in edges[node]:
                dfs(v)
                value[node] += value[v]
                cnt_node[node] += cnt_node[v]
            if value[node] == 0:
                cnt_node[node] = 0
        dfs(0)
        return cnt_node[0]

758、字符串加粗单词

class Solution:
    def boldWords(self, words: List[str], s: str) -> str:
        n = len(s)
        mask = [False for _ in range(n)]
        for i in range(n):
            for word in words:
                if i+len(word)<=n and s[i:i+len(word)]==word:
                    for j in range(len(word)):
                        mask[i+j] = True
        res = ''
        for idx, x in enumerate(mask):
            if mask[idx] and (idx==0 or mask[idx-1]==False):
                res+=''
            res+=s[idx]
            if mask[idx] and (idx==n-1 or mask[idx+1]==False):
                res+=''
        return res

1062、最长重复子串

二分查找 binary-search

class Solution:
    def boldWords(self, words: List[str], s: str) -> str:
        n = len(s)
        mask = [False for _ in range(n)]
        for i in range(n):
            for word in words:
                if i+len(word)<=n and s[i:i+len(word)]==word:
                    for j in range(len(word)):
                        mask[i+j] = True
        res = ''
        for idx, x in enumerate(mask):
            if mask[idx] and (idx==0 or mask[idx-1]==False):
                res+=''
            res+=s[idx]
            if mask[idx] and (idx==n-1 or mask[idx+1]==False):
                res+=''
        return res

LCP 12. 小张刷题计划

class Solution:
    def minTime(self, time: List[int], m: int) -> int:
        left, right = 0, sum(time)
        while left<right:
            L = (left+right)//2
            if self.isTime(L, time, m):
                right = L
            else:
                left = L+1
        return left
    def isTime(self, Limit, time, m):
        cur_time = 0
        max_time = time[0]
        days = 1
        for x in time[1:]:
            if cur_time + min(max_time, x)<=Limit:
                cur_time += min(max_time,x)
                max_time = max(max_time, x)
            else:
                cur_time = 0
                days += 1
                # max_time = min(max_time, x)
                max_time = x
        if days>m:
            return False
        else:
            return True

625、最小因式分解

给定一个正整数 a,找出最小的正整数 b 使得 b 的所有数位相乘恰好等于 a。

如果不存在这样的结果或者结果不是 32 位有符号整数,返回 0。

class Solution:
    def smallestFactorization(self, num: int) -> int:
        res = 0
        weight = 1
        if num<=9:
            return num
        for x in range(9,1,-1):
            while num%x == 0:
                num //= x
                res = weight*x + res
                weight *= 10
                if res >= 2**31-1:
                    return 0
        if num!=1:
            return 0
        return res

750、角矩形的数量

动态规划
给定一个只包含 0 和 1 的 m x n 整数矩阵 grid ,返回 其中 「角矩形 」的数量 。

一个「角矩形」是由四个不同的在矩阵上的 1 形成的 轴对齐 的矩形。注意只有角的位置才需要为 1。
注意:4 个 1 的位置需要是不同的。

链接:https://leetcode.cn/problems/number-of-corner-rectangles

class Solution:
    def countCornerRectangles(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dp = [[0 for _ in range(n)] for _ in range(n)]
        res = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j]==1:
                    for k in range(j+1,n):
                        if grid[i][k]==1:
                            res+=dp[j][k]
                            dp[j][k]+=1
        return res

三、困难

305、岛屿数量二

并查集的应用
给你一个大小为 m x n 的二进制网格 grid 。网格表示一个地图,其中,0 表示水,1 表示陆地。最初,grid 中的所有单元格都是水单元格(即,所有单元格都是 0)。

可以通过执行 addLand 操作,将某个位置的水转换成陆地。给你一个数组 positions ,其中 positions[i] = [ri, ci] 是要执行第 i 次操作的位置 (ri, ci) 。

返回一个整数数组 answer ,其中 answer[i] 是将单元格 (ri, ci) 转换为陆地后,地图中岛屿的数量。

岛屿 的定义是被「水」包围的「陆地」,通过水平方向或者垂直方向上相邻的陆地连接而成。你可以假设地图网格的四边均被无边无际的「水」所包围。

链接:https://leetcode.cn/problems/number-of-islands-ii

class FindUnion:
    def __init__(self,n):
        self.father = [x for x in range(n)]
        self.cluster = 0
        self.size = [1 for _ in range(n)]
    def findFather(self,x):
        while x!=self.father[x]:
            self.father[x] = self.father[self.father[x]]
            x = self.father[x]
        return x
    def Union(self,x,y):
        root_x = self.findFather(x)
        root_y = self.findFather(y)
        if root_x == root_y:
            return True
        else:
            if root_x > root_y:
                self.father[root_x] = root_y
                self.size[root_x] += self.size[root_y]
            else:
                self.father[root_y] = root_x
                self.size[root_y] += self.size[root_x]
            self.cluster -= 1
            return True

class Solution:
    def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]:
        uf = FindUnion(m*n)
        seen = set()
        res = []
        for x,y in positions:
            ID = x*n+y
            if ID in seen:
                res.append(uf.cluster)
                continue
            seen.add(ID)
            uf.cluster += 1
            for nr, nc in [(x+1,y),(x-1,y),(x,y+1),(x,y-1)]:
                if 0<=nr<m and 0<=nc<n:
                    Id2 = nr*n+nc
                    if Id2 in seen:
                        uf.Union(ID, Id2)
            res.append(uf.cluster)
        return res

124、二叉树中的最大路径和

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

链接:https://leetcode.cn/problems/binary-tree-maximum-path-sum

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        self.res = -float('inf')
        def dfs(root):
            if not root: return 0
            left = dfs(root.left)
            right = dfs(root.right)
            self.res=max(self.res,root.val+left+right, root.val+left, root.val+right, root.val)
            print(root.val, self.res)
            return max(root.val+left, root.val+right, root.val)
        dfs(root)
        return self.res

72、编辑距离

动态规划
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符

链接:https://leetcode.cn/problems/edit-distance

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        dp = [[0 for _ in range(n+1)]for _ in range(m+1)]
        for i in range(m+1):
            dp[i][0] = i
        for i in range(n+1):
            dp[0][i] = i
        for i in range(1,m+1):
            for j in range(1,n+1):
                left = dp[i][j-1] + 1
                right = dp[i-1][j] + 1
                lr = dp[i-1][j-1]
                if word1[i-1]!=word2[j-1]:
                    lr += 1
                dp[i][j] = min(left, right, lr)
        return dp[m][n]

912、排序数组

快排

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        def getIndex(nums, left, right):
            p = nums[left]
            while left < right:
                while left < right and p<=nums[right]:
                    right -= 1
                nums[left] = nums[right]
                while left < right and p>=nums[left]:
                    left += 1
                nums[right] = nums[left]
            nums[left] = p
            return left
        def randomIndex(nums, left, right):
            index = random.randint(left, right)
            nums[left], nums[index] = nums[index],nums[left]
            return getIndex(nums,left,right)

        def quickSort(nums, left, right):
            if left<right:
                mid = randomIndex(nums,left,right)
                quickSort(nums, left, mid-1)
                quickSort(nums, mid+1, right)
        quickSort(nums,0,len(nums)-1)
        return nums

你可能感兴趣的:(leetcode专栏,leetcode,算法)