leetcode刷题记录801-810 python版

前言

继续leetcode刷题生涯
这里记录的都是笔者觉得有点意思的做法
参考了好几位大佬的题解,感谢各位大佬

801. 使序列递增的最小交换次数

class Solution:
    def minSwap(self, A: List[int], B: List[int]) -> int:
        length = len(A)
        dp_swap = [float("inf")] * length
        dp_keep = [float("inf")] * length

        # 初始化
        dp_swap[0] = 1
        dp_keep[0] = 0

        # 状态转移
        for i in range(1, length):
            if A[i] > A[i - 1] and B[i] > B[i - 1]:
                dp_swap[i] = dp_swap[i - 1] + 1
                dp_keep[i] = dp_keep[i - 1]
            if A[i] > B[i - 1] and B[i] > A[i - 1]:
                dp_swap[i] = min(dp_keep[i - 1] + 1, dp_swap[i])
                dp_keep[i] = min(dp_swap[i - 1], dp_keep[i])
        # 输出结果

        return min(dp_keep[-1], dp_swap[-1])

802. 找到最终的安全状态

# 拓扑排序
class Solution:
    def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
        import collections
        res = []
        idx = collections.defaultdict(list) #入度表
        for i in range(len(graph)):
            if not graph[i]:        #无出度的加入输出
                res += [i]
            for j in graph[i]:      #生成入度表
                idx[j] += [i]
        for j in res:               #遍历无出度点
            for i in idx[j]:        #遍历该点的入度点
                graph[i].remove(j)  #删除出度
                if not graph[i]:    #如果没有出度了
                    res += [i]      #加入输出
        return sorted(res)

803. 打砖块

class DSU:
    def __init__(self, R, C):
        #R * C is the source, and isn't a grid square
        self.par = list(range(R*C + 1)) # [0, 1, 2, 3, 4, 5, 6, 7, 8],并查集初始化各自为各自的集合
        self.rnk = [0] * (R*C + 1) # 相当于合并次数
        self.sz = [1] * (R*C + 1) # 表示集合中节点的数目,由于初始化每个节点都在自己的集合,所以都是1

    def find(self, x):
        if self.par[x] != x: # find终止条件就是一直向上find,直到parent[x]=x,自己就是代表节点
            self.par[x] = self.find(self.par[x]) # 递归查找
        return self.par[x]

    def union(self, x, y):
        xr, yr = self.find(x), self.find(y)
        if xr == yr: return
        if self.rnk[xr] < self.rnk[yr]:
            xr, yr = yr, xr
        if self.rnk[xr] == self.rnk[yr]:
            self.rnk[xr] += 1

        self.par[yr] = xr
        self.sz[xr] += self.sz[yr] # 集合节点数量合并

    def size(self, x):
        return self.sz[self.find(x)] # 表示集合中节点的数目

    def top(self): # 求顶部那个集合的大小
        # Size of component at ephemeral "source" node at index R*C,
        # minus 1 to not count the source itself in the size
        return self.size(len(self.sz) - 1) - 1 # len(self.sz) - 1其实就是R*C

class Solution(object):
    def hitBricks(self, grid, hits):
        R, C = len(grid), len(grid[0])

        def index(r, c): # 将坐标(x,y)转化为index
            return r * C + c

        def neighbors(r, c): # 迭代生成符合题意的邻居节点
            for nr, nc in ((r-1, c), (r+1, c), (r, c-1), (r, c+1)):
                if 0 <= nr < R and 0 <= nc < C:
                    yield nr, nc

        A = [row[:] for row in grid] # [[1, 0, 0, 0], [1, 1, 1, 0]],其实就是grid,相当于深拷贝了
        for i, j in hits: # 先去掉hits里面所有位置的砖头
            A[i][j] = 0

        dsu = DSU(R, C) # 下面开始构造不考虑hits情况下的并查集
        for r, row in enumerate(A):
            for c, val in enumerate(row): # (r,c)相当于位置,val为当前位置的值
                if val: # 该位置有砖头
                    i = index(r, c) # 坐标转换为index,因为使用list实现的并查集
                    if r == 0: # 是第一行顶部
                        dsu.union(i, R*C) # 合并到R*C一派,R*C是标记的顶部集合,我们定义的,因为最大index是R*C-1,设成别的也ok
                    if r and A[r-1][c]: # 不是第一行并且上一行有砖头
                        dsu.union(i, index(r-1, c)) # 合并到上一行那一派
                    if c and A[r][c-1]: # 不是最左边并且左边有砖头
                        dsu.union(i, index(r, c-1)) # 合并到最左边
        # print(dsu.par) # [0, 1, 2, 3, 4, 6, 6, 7, 0] # 这样看并查集内容,你可以放在上面循环里面打印

        ans = []
        for r, c in reversed(hits): # 反向思考,如果我们是往上放砖,
            # 为什么要反向?因为如果是拿砖,后一次会影响前一次,放砖就是前一次被后一次影响
            pre_roof = dsu.top() # 保存并查集现在的状态
            if grid[r][c] == 0: # 此处无砖,结果不变
                ans.append(0)
            else:
                i = index(r, c) # 若此处有砖,求坐标
                for nr, nc in neighbors(r, c): # 遍历四周连接砖头
                    if A[nr][nc]: # 如果有砖
                        dsu.union(i, index(nr, nc)) #归到一类
                if r == 0:
                    dsu.union(i, R*C) # 如果是顶部,归到顶部自定义的R*C类
                A[r][c] = 1 # 还原A中此处的砖头,A之前是不考虑hits处有砖的grid,必须要还原,我们是在A上计算的,也就是说hits中后面的会影响前面的
                ans.append(max(0, dsu.top() - pre_roof - 1)) # 变化后状态-变化前的状态-当前砖块的1,因为这个砖是hits加上去的
        return ans[::-1] # 结果反转,因为前面hit是反转的

if __name__ == "__main__":
    grid = [[1, 0, 0, 0], [1, 1, 1, 0]]
    hits = [[1, 0]]
    s = Solution()
    print(s.hitBricks(grid, hits))

804. 唯一摩尔斯密码词

class Solution:
    def uniqueMorseRepresentations(self, words: List[str]) -> int:
        dic = {'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....',
               'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.',
               'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-',
               'y': '-.--', 'z': '--..'}
        res = set()
        for word in words:
            tmp = ''
            for i in word:
                tmp += dic[i]
            res.add(tmp)
        return len(res)

805. 数组的均值分割

class Solution:
    def splitArraySameAverage(self, A: List[int]) -> bool:  # 每个列表的平均数与A的平均数相等
        A.sort()
        n = len(A)
        if n < 2: return False
        ave = sum(A) / n
        def findlist(A, i, s):
            ll = len(A)
            if i == 0 and s == 0:
                return True
            if i <= 0:
                return False
            for j in range(ll - i + 1):  # 保证第一个数字位置加上总位数小于A的长度
                if j > 0 and A[j] == A[j - 1]:  # 除去重复的情况
                    continue
                if findlist(A[j + 1:], i - 1, s - A[j]):
                    return True
            return False
        for i in range(1, n // 2 + 1):  # 列表长度
            if abs(i * ave - int(i * ave)) > 1e-10:  # 整数和必须为整数 可能引入无限小数
                continue
            if findlist(A, i, int(i * ave)):
                return True
        return False

806. 写字符串需要的行数


class Solution:
    def numberOfLines(self, widths: List[int], S: str) -> List[int]:
        cnt = 0
        row = 0
        for s in S:
            cnt += widths[ord(s) - 97]
            if cnt == 100:
                row += 1
                cnt = 0
            elif cnt > 100:
                row += 1
                cnt = widths[ord(s) - 97]
        return [row + 1, cnt]

807. 保持城市天际线

class Solution:
    def maxIncreaseKeepingSkyline(self, grid: List[List[int]]) -> int:
        n = len(grid)
        row = [max(grid[i]) for i in range(n)]
        col = [max(grid[i][j] for i in range(n)) for j in range(n)]
        res = 0
        for i in range(n):
            for j in range(n):
                res += min(row[i], col[j]) - grid[i][j]
        return res

808. 分汤

class Solution:
    def soupServings(self, N: int) -> float:
        if N > 4800: return 1
        dic = dict()
        def solve(A, B):
            if (A, B) in dic:
                return dic[(A, B)]
            if A <= 0 and B > 0:
                res = 1
            elif A <= 0 and B <= 0:
                res = 0.5
            elif A > 0 and B <= 0:
                res = 0
            else:
                res = 0.25 * (solve(A - 100, B) + solve(A - 75, B - 25) + solve(A - 50, B - 50) + solve(A - 25, B - 75))
            dic[(A, B)] = res
            return res
        return solve(N, N)

809. 情感丰富的文字

class Solution:
    def expressiveWords(self, S: str, words: List[str]) -> int:
        res = 0  # 可扩张的单词数量
        counter_S = self.getCounter(S)
        n = len(counter_S)
        for word in words:
            counter_W = self.getCounter(word)
            if len(counter_W) != n:
                # 如果计数数组长度都不一样,直接判断不能扩张,对下一个单词进行判断
                continue
            for i in range(n):
                if counter_S[i][0] != counter_W[i][0]:
                    # 如果对应位置字母不相同,则不能扩张,跳出循环
                    break
                if counter_W[i][1] == 1 and (counter_S[i][1] >= 3 or counter_S[i][1] == 1):
                    # 字母计数为1,对应的位置可以不扩张=1,也可以扩张 >=3
                    pass
                elif counter_W[i][1] == 2 and (counter_S[i][1] >= 3 or counter_S[i][1] == 2):
                    # 字母计数为2,对应的位置可以不扩张=2,也可以扩张 >=3
                    pass
                elif counter_W[i][1] >= 3 and counter_S[i][1] >= counter_W[i][1]:
                    # 字母计数超过3的情况,对应的位置,只需满足扩张后的计数比原来多即可
                    pass
                else:
                    # 剩下的情况是不合法的扩张,直接跳出循环
                    break
                if i == len(counter_S) - 1:
                    # 如果遍历到最后一个字母,仍符合扩张要求,则单词计数 +1
                    res += 1
                    # print(word)
        return res
    def getCounter(self, S: str) -> list:
        counter = []
        for w in S:
            if counter == []:  # 初始情况
                counter.append([w, 1])
            else:
                if w == counter[-1][0]:
                    # 和前面的字母一样,那么计数+1
                    counter[-1][1] += 1
                else:
                    # 不一样,就是新的尾字母
                    counter.append([w, 1])
        return counter

# 正则
import re  # use re module to process regular expression matching problems
class Solution:
    def expressiveWords(self, S: str, words) -> int:
        pattern = '^'  # '^' match the start of target word
        a = [i for i in range(len(S)) if i == 0 or S[i] != S[i-1]] + [len(S)]  # record the start indexes of continuous same letter; e.g.'aaabbcc': [0, 3, 5]
        for i in range(1, len(a)):
            cha, num = S[a[i]-1], a[i] - a[i-1]  # cha is the continuous same letters, num is length
            print(cha, num)
            if num < 3:
                pattern += cha * num  # if num < 3, it means not stretchy, so the regular expression should be this 'cha' itself
            if num >= 3:
                pattern += '%s{1,%d}' % (cha, num)  # if num > 3, it means any length that not longer than num (but need to exist) could be OK, so the regular expression is '%s{1,%d}'
        pattern += '$' # '$' match the end of target word
        res = 0 # use res as a count
        for word in words:
            if re.match(pattern, word):
                res += 1 # use regular expression to match word in words one by one
        return res

810. 黑板异或游戏

class Solution:
    def xorGame(self, nums: List[int]) -> bool:
        if len(nums) % 2 == 0:
            return True

        xor_val = 0
        for val in nums:
            xor_val ^= val

        return xor_val == 0

你可能感兴趣的:(leetcode)