Leetcode

  • [30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
  • [798. 得分最高的最小轮调](https://leetcode-cn.com/problems/smallest-rotation-with-highest-score/)
  • 1601. 最多可达成的换楼请求数目
  • 564. 寻找最近的回文数
  • [1719. 重构一棵树的方案数](https://leetcode-cn.com/problems/number-of-ways-to-reconstruct-a-tree/)
  • [2019. 解出数学表达式的学生分数](https://leetcode-cn.com/problems/the-score-of-students-solving-math-expression/)


30. 串联所有单词的子串

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        ## 方法一:全排列 超时
        # n = len(words)
        # res = []
       
        # for p in permutations(words, n):   
        #     i = "".join(p)        
        #     idx = -1
        #     while True:
        #         idx = s.find(i, idx + 1)
        #         if idx == -1:break
                
        #         res.append(idx)
                  
        # return list(set(res))

        ## 方法二:Counter
        # from collections import Counter
        def f(x):
            d = defaultdict(int)
            for s in x: d[s] += 1
            return d

        res = []
        wordLen = len(words[0])
        totalLen = len(words) * wordLen
        n = len(s)
        words = f(words)

        for i in range(n - totalLen + 1):
            sub = s[i:i + totalLen] # 依次截取总长子串,移动窗口。
            tmp = [sub[j:j + wordLen] for j in range(0, totalLen, wordLen)] # 切割总子串为等长子串

            if f(tmp) == words: # 单词与个数都相同,正好匹配。
                res.append(i)
                
        return res

798. 得分最高的最小轮调

最简单的做法是遍历每个可能的 k,计算轮调 k 个位置之后的数组得分。假设数组的长度是 n,则有 n 种可能的轮调,对于每种轮调都需要 O(n) 的时间计算得分,总时间复杂度是 O(n2),对于 n ≤ 105 的数据范围会超出时间限制,因此需要优化。

class Solution:
    def bestRotation(self, nums: List[int]) -> int:
        n, a, score, index = len(nums), nums * 2, 0, 0        
        for k in range(n):
            t = sum(e <= i for i, e in enumerate(a[k : n + k]))           
            if t > score: index, score = k, t              
        return index

nums[i] = x,当 i ≥ x,即 i ∈ [x, n) 时,得 1 分。
轮调 k 次后,下标为 i - k,当新下标为负数时,相当于 nums[i] 出现在比原数组更“靠后”的位置,此时下标等价于 (i - k + n) % n。

class Solution:
    def bestRotation(self, nums: List[int]) -> int:
        n = len(nums)
        diffs = [0] * n
        for i, num in enumerate(nums):
            low = (i + 1) % n
            high = (i - num + n + 1) % n
            diffs[low] += 1
            diffs[high] -= 1
            if low >= high:
                diffs[0] += 1
        score, maxScore, idx = 0, 0, 0
        for k, diff in enumerate(diffs):
            score += diff
            if score > maxScore:
                maxScore, idx = score, k
        return idx

1601. 最多可达成的换楼请求数目

Leetcode 过滤掉同楼的请求
从大到小枚举最多成立的请求数

class Solution:
    def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
        m = len(requests)
        requests = list(filter(lambda x:x[0] != x[1], requests))      
        m -= len(requests)

        for i in range(len(requests), 0, -1):
            # 从 requests 里选取 i 个的所有组合
            for comb in combinations(requests, i):                
                # 统计出度入度是否完全相等
                cnts = [0] * n
                for a, b in comb:
                    cnts[a] += 1 # 出度
                    cnts[b] -= 1 # 入度
                if all(not c for c in cnts):
                    return i + m      
                
        return m

        ## 方法二:二进制枚举
        # 用一个长度为 m 的二进制数 mask 表示所有的请求,
        # 其中 mask 从低到高的第 i 位为 1 表示选择第 i 个请求,为 0 表示不选第 i 个请求。
        # 枚举 [0, 2^m-1] 范围内的所有 mask,对于每个mask,依次枚举其每一位,判断是否为 1,
        # 并使用与方法一相同的数组 delta 以及变量 cnt 进行统计,在满足要求时更新答案。
        ans = 0
        for mask in range(1 << len(requests)):
            cnt = mask.bit_count() # 选择数
            if cnt <= ans: continue
            delta = [0] * n
            for i, (x, y) in enumerate(requests):
                if mask & (1 << i):
                    delta[x] += 1
                    delta[y] -= 1
            if all(x == 0 for x in delta):
                ans = cnt
        return ans + m

564. 寻找最近的回文数

Leetcode 小于等于10 的数,返回 n-1
10 的幂,返回 n-1
若干个 9,返回 n+2
11,这个数字比较特殊,返回 9

首先把 n 从中间分成 a、b 两部分,如果长度是奇数就给 a 多分点。

然后用 a、a+1、a-1 为左边分别构建一个回文数,注意n长度为奇数的时候左边的最后一个字符不能复制过去。

最后选取离 n 最近且不为 n 的结果即可

class Solution:
    def nearestPalindromic(self, n: str) -> str:
        if int(n) < 10 or int(n[::-1]) == 1:
            return str(int(n) - 1)
        if n == '11': return '9'
        if set(n) == {'9'}: return str(int(n) + 2)
        x = (len(n) + 1) // 2
        a, b = n[:x], n[x:] # 偶数对半,奇数 a 少 b 多
        temp = [str(int(a) - 1), a, str(int(a) + 1)]
        temp = [i + i[len(b) - 1::-1] for i in temp]
        return min(temp, key = lambda x:abs(int(x) - int(n)) or float('inf'))

1719. 重构一棵树的方案数

2019. 解出数学表达式的学生分数

op = {'+': add, '*': mul}
@lru_cache(None)
def dfs(s):
    return {s[0]} if len(s) == 1 else\
           set([op[s[i]](a, b) for i in range(1, len(s), 2) for a in dfs(s[:i]) for b in dfs(s[i + 1:]) if op[s[i]](a, b) <= 1000])
class Solution:
    def scoreOfStudents(self, s: str, answers: List[int]) -> int:
        correct = eval(s)
        d = dfs(tuple(ch if ch in ('+', '*') else int(ch) for ch in s))
        return sum(5 if x == correct else 2 for x in answers if x in d)