这周题目有点多。。周赛、双周赛还有每日一题。。做的慢有点做不过来了
第一题:上升下降字符串
给你一个字符串 s ,请你根据下面的算法重新构造字符串:
从 s 中选出 最小 的字符,将它 接在 结果字符串的后面。
从 s 剩余字符中选出 最小 的字符,且该字符比上一个添加的字符大,将它 接在 结果字符串后面。
重复步骤 2 ,直到你没法从 s 中选择字符。
从 s 中选出 最大 的字符,将它 接在 结果字符串的后面。
从 s 剩余字符中选出 最大 的字符,且该字符比上一个添加的字符小,将它 接在 结果字符串后面。
重复步骤 5 ,直到你没法从 s 中选择字符。
重复步骤 1 到 6 ,直到 s 中所有字符都已经被选过。
在任何一步中,如果最小或者最大字符不止一个 ,你可以选择其中任意一个,并将其添加到结果字符串。
请你返回将 s 中字符重新排序后的 结果字符串 。
示例 1:
输入:s = “aaaabbbbcccc”
输出:”abccbaabccba”
解释:第一轮的步骤 1,2,3 后,结果字符串为 result = “abc”
第一轮的步骤 4,5,6 后,结果字符串为 result = “abccba”
第一轮结束,现在 s = “aabbcc” ,我们再次回到步骤 1
第二轮的步骤 1,2,3 后,结果字符串为 result = “abccbaabc”
第二轮的步骤 4,5,6 后,结果字符串为 result = “abccbaabccba”
示例 2:
输入:s = “rat”
输出:”art”
解释:单词 “rat” 在上述算法重排序以后变成 “art”
示例 3:
输入:s = “leetcode”
输出:”cdelotee”
示例 4:
输入:s = “ggggggg”
输出:”ggggggg”
示例 5:
输入:s = “spo”
输出:”ops”
思路:
统计每个小写字母的个数,然后从前往后再从后往前依次取即可。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class Solution: def sortString(self, s: str) -> str: l = len(s) if l == 0: return '' lowercases = string.ascii_lowercase counts = [0 for i in range(26)] for i in range(26): counts[i] = s.count(lowercases[i]) # 统计每个小写字母的个数 ans = '' left_to_right = True # 是否从左往右取 while l: if left_to_right: for i in range(26): if counts[i]: l -= 1 counts[i] -= 1 ans += lowercases[i] else: for i in range(25, -1, -1): if counts[i]: l -= 1 counts[i] -= 1 ans += lowercases[i] left_to_right = not left_to_right # 反向 return ans |
第二题:每个元音包含偶数次的最长子字符串
给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 ‘a’,’e’,’i’,’o’,’u’ ,在子字符串中都恰好出现了偶数次。
示例 1:
输入:s = “eleetminicoworoep”
输出:13
解释:最长子字符串是 “leetminicowor” ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。
示例 2:
输入:s = “leetcodeisgreat”
输出:5
解释:最长子字符串是 “leetc” ,其中包含 2 个 e 。
示例 3:
输入:s = “bcbcbc”
输出:6
解释:这个示例中,字符串 “bcbcbc” 本身就是最长的,因为所有的元音 a,e,i,o,u 都出现了 0 次。
思路:
这题放在第二题有点难了。。。卡了很久。。。 5个元音字母a
,e
,i
,o
,u
,出现的次数要么为奇数次,要么为偶数次
。因此总共有2^5=32
种状态。
统计从开头开始到位置i
每个字母的数量,用0-31
分别表示每个位置的状态。
状态相同的两个位置
之间元音字母必然出现偶数次,从开头到状态为0
的位置字母必然出现偶数次。
记录每种状态status
的最早出现位置firsts[status]
和最后出现位置lasts[status]
。
维护一个全局变量ans
,遍历每种状态,当lasts[status]
和firsts[status]
均存在且lasts[status] - firsts[status] > ans
时更新ans
。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class Solution: def findTheLongestSubstring(self, s: str) -> int: letters = ['a', 'e', 'i', 'o', 'u'] orders = [16, 8, 4, 2, 1] firsts = [-1 for i in range(32)] lasts = [-1 for i in range(32)] l = len(s) ans = 0 letters_dict = {i: True for i in letters} firsts[0] = 0 for i in range(l): c = s[i] if c in letters: letters_dict[c] = not letters_dict[c] # 用True和False记录奇偶 bases = [0 if v else 1 for v in letters_dict.values()] status = 0 for j in range(5): status += bases[j] * orders[j] # 位置i的状态 if firsts[status] == -1: firsts[status] = i + 1 lasts[status] = i + 1 for i in range(32): if firsts[i] != -1 and lasts[i] != -1: ans = max(ans, lasts[i] - firsts[i]) return ans |
第三题:二叉树中的最长交错路径
给你一棵以 root 为根的二叉树,二叉树中的交错路径定义如下:
选择二叉树中 任意 节点和一个方向(左或者右)。
如果前进方向为右,那么移动到当前节点的的右子节点,否则移动到它的左子节点。
改变前进方向:左变右或者右变左。
重复第二步和第三步,直到你在树中无法继续移动。
交错路径的长度定义为:访问过的节点数目 – 1(单个节点的路径长度为 0 )。
请你返回给定树中最长 交错路径 的长度。
示例 1:
输入:root = [1,null,1,1,1,null,null,1,1,null,1,null,null,null,1,null,1]
输出:3
解释:蓝色节点为树中最长交错路径(右 -> 左 -> 右)。
示例 2:
输入:root = [1,1,1,null,1,null,null,1,1,null,1]
输出:4
解释:蓝色节点为树中最长交错路径(左 -> 右 -> 左 -> 右)。
示例 3:
输入:root = [1]
输出:0
思路:
dfs。在搜索过程中分别记录从每个结点结点点开始向左走、向右走以及从该节点的子节点开始走的最长路径。
某个结点的最长交错路径
= max(以该结点为起始点往左走的最长交错路径
,以该结点为起始点往右走的最长交错路径
,左右子结点的最长交错路径中的最大值
)。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Solution: def help(self, root: TreeNode): if not root: return (0, 0, 0) l, r, d= 0, 0, 0 # 分别表示该节点开始向左走、向右走以及从该节点的子节点开始走的最长路径 if root.left: tmp = self.help(root.left) l = tmp[1] + 1 # 左子结点再向右走的最长路径 d = max(tmp) # d表示不考虑根结点,只考虑左右子结点的结果 if root.right: tmp = self.help(root.right) r = tmp[0] + 1 # 右子结点再向左走的最长路径 d = max(d, max(tmp)) return (l, r, d) def longestZigZag(self, root: TreeNode) -> int: l, r, d = self.help(root) return max(l, r, d) |
第四题:二叉搜索子树的最大键值和
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。
二叉搜索树的定义如下:
任意节点的左子树中的键值都 小于 此节点的键值。
任意节点的右子树中的键值都 大于 此节点的键值。
任意节点的左子树和右子树都是二叉搜索树。
示例 1:
输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。
示例 2:
输入:root = [4,3,null,1,2]
输出:2
解释:键值为 2 的单节点子树是和最大的二叉搜索树。
示例 3:
输入:root = [-4,-2,-5]
输出:0
解释:所有节点键值都为负数,和最大的二叉搜索树为空。
示例 4:
输入:root = [2,1,3]
输出:6
示例 5:
输入:root = [5,4,8,3,null,6,3]
输出:7
思路:
二叉搜索树🌲具有以下重要性质:如果一棵树为二叉搜索树,则它的左右子树也为二叉搜索树。
dfs。在遍历每个结点是时分别计算:该树的最小值
,该树的最大值
,该树的键值和
,该树是否为二叉搜索树
。
其中,该树的最小值
= min(左子树的最小值
,右子树的最小值
,根结点的值
)。
该树的最大值
= max(左子树的最大值
,右子树的最大值
,根结点的值
)。
该树的键值和
= sum(左子树的键值和
,右子树的键值和
,根结点的值
)。
该树是否为二叉搜索树
= 左子树是否为二叉搜索树
and 右子树是否为二叉搜索树
and 左子树的最大值小于根结点
and 右子树的最小值大于根结点
。
用全局变量ans
记录二叉搜索树的最大键值和,如果搜索🔍过程中找到一棵二叉搜索树,且其键值和大于ans
,则更新ans
。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
class Solution: def dfs(self, root: TreeNode): assert root min_root = root.val max_root = root.val sum_root = root.val is_search_tree = True if root.left: min_left, max_left, sum_left, is_search_tree_left = self.dfs(root.left) min_root = min(min_root, min_left) max_root = max(max_root, max_left) sum_root += sum_left if max_left >= root.val or not is_search_tree_left: is_search_tree = False if root.right: min_right, max_right, sum_right, is_search_tree_right = self.dfs(root.right) min_root = min(min_root, min_right) max_root = max(max_root, max_right) sum_root += sum_right if min_right <= root.val or not is_search_tree_right: is_search_tree = False if is_search_tree: self.ans = max(self.ans, sum_root) # print(root.val, sum_root) return min_root, max_root, sum_root, is_search_tree def maxSumBST(self, root: TreeNode) -> int: self.ans = 0 if not root: return 0 self.dfs(root) return self.ans |