LeetCode - 相同子树替换后最长重复子串多米诺骨牌
Posted SpikeKing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode - 相同子树替换后最长重复子串多米诺骨牌相关的知识,希望对你有一定的参考价值。
多米诺骨牌
838. 推多米诺,注意多米诺骨牌是连续倒,直到停止。
考察
- 先入先出队列(deque),处理时序问题。
- 时序数组和状态数组
#!/usr/bin/env python
# -- coding: utf-8 --
"""
Copyright (c) 2022. All rights reserved.
Created by C. L. Wang on 2022/5/31
"""
from collections import deque
class Solution:
def pushDominoes(self, dominoes: str) -> str:
n = len(dominoes)
# 使用[]会超时,只能使用deque
queue = deque()
time = [-1] * n # 模拟当前时刻, -1游戏未开始
force = [[] for _ in range(n)] # 保存当前点受力
# 初始化, 即0时刻, 状态
for i, f in enumerate(dominoes):
if f != ".":
queue.append(i)
time[i] = 0
force[i].append(f)
res = ["."] * n
while queue:
i = queue.popleft()
# 只有1个受力, 如果有2个受力, 该点一定不动, LR和RL都是如此
if len(force[i]) == 1:
res[i] = f = force[i][0] # 结果确定
ni = i - 1 if f == 'L' else i + 1 # L向左,R向右
if 0 <= ni < n:
t = time[i]
if time[ni] == -1: # 第1次受到作用力
queue.append(ni)
time[ni] = t + 1
force[ni].append(f)
elif time[ni] == t + 1: # 如果同时受到作用力, 被改变
force[ni].append(f)
return ''.join(res)
def main():
so = Solution()
res = so.pushDominoes(".L.R.....LR..L..")
print("res: ".format(res)) # LL.RRR.LLLRRLL..
if __name__ == '__main__':
main()
替换后最长重复子串
考察:
- 双指针的用法
- 滑动窗口长度,满足条件,则递增,不满足条件,则不变
- 辅助变量,最大出现次数,只保存最大值,单调递增
- 统计窗口中出现值的次数。
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
"""
424. 替换后的最长重复字符
时间复杂度O(n),遍历1次
空间复杂度O(C),C是字符集
通过双指针的滑动窗口进行确定
统计窗口中每个字母的出现次数,获取最大出现次数
比较:字符串长度的关系 与 最大出现次数 + k
维护历史最大值的原因:窗口从根本来说是只增不减的:
如果k够用来替换的话,right右移1步,窗口变大(当前维护的最大值变大,不消耗k;当前维护的最大值不变,将消耗k);
如果k不够用来替换的话,left和right都右移一步,窗口不变(我们已经不关心比当前小的情况了)。
"""
if not s:
return 0
# 窗口中的出现次数
num = [0] * 26 # 26个大写字母
n = len(s)
maxn, left, right = 0, 0, 0
# 以右指针为基准
while right < n:
rc = ord(s[right]) - ord("A")
lc = ord(s[left]) - ord("A")
num[rc] += 1 # 求窗口中曾出现某字母的最大次数
maxn = max(maxn, num[rc]) # 最大出现次数,递增
s_len = right - left + 1 # 字符串长度
if s_len > k + maxn: # 不满足条件
num[lc] -= 1
left += 1
right += 1 # 遍历
return right - left
def main():
so = Solution()
res = so.characterReplacement("AACBAKBBA", 2)
print("res: ".format(res)) # 5,替换CB为AA
if __name__ == '__main__':
main()
相同题目:2024. 考试的最大困扰度
class Solution:
def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
"""
参考: 424. 替换后的最长重复字符
双指针遍历一次
时间复杂度:O(N)
空间复杂度:O(N),存储次数的数组
"""
if not answerKey:
return 0
nums = [0] * 2
maxn, left, right = 0, 0, 0
n = len(answerKey)
while right < n:
rk = 0 if answerKey[right] == "T" else 1
nums[rk] += 1
maxn = max(maxn, nums[rk])
if right-left+1 > maxn + k:
lk = 0 if answerKey[left] == "T" else 1
nums[lk] -= 1
left += 1
right += 1
return right - left
def main():
so = Solution()
res = so.maxConsecutiveAnswers("TTFTTFFTT", 1)
print("res: ".format(res)) # 5,替换第3个F为T
if __name__ == '__main__':
main()
相同子树
树:[1,2,3,4,null,2,4,null,null,4],注意最后一层只有非空节点,才有null值
考察:
- 构建二叉树,二叉树是含义叶子节点的满二叉树,需要判断非空节点。
- 遍历二叉树,也需要去除最后的连续空值。
- 考察字典的用法。
构建和遍历,细节较多。
import collections
# 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
def build_tree(alist):
"""
构建二叉树
"""
n = len(alist)
root = TreeNode(alist[0])
queue = [root]
i = 1
while queue:
cur = queue.pop(0)
if cur:
if i < n and alist[i]: # 非空节点加入
cur.left = TreeNode(alist[i])
queue.append(cur.left)
i += 1
if i < n and alist[i]: # 非空节点加入
cur.right = TreeNode(alist[i])
queue.append(cur.right)
i += 1
return root
def tree2list(root):
"""
树转换为list
"""
res = []
queue = [root]
while queue:
node = queue.pop(0)
res.append(node.val)
if not node.val: # 空节点
continue
if node.left:
queue.append(node.left)
else:
queue.append(TreeNode(None))
if node.right:
queue.append(node.right)
else:
queue.append(TreeNode(None))
# 去除最后的None
last = 0
for i, val in enumerate(res):
if val:
last = i
return res[:last+1]
class Solution:
def findDuplicateSubtrees(self, root):
"""
652. 寻找重复的子树
字典 + 元组,递归遍历左右节点
时间复杂度:O(N),其中N二叉树上节点的数量,每个节点都需要访问一次。
空间复杂度:O(N),每棵子树的存储空间都为 O(1)。
"""
if not root:
return []
# 相当于给每个元组一个固定编号,从1开始,依次排列
# 也可以不适用,则耗时变长
uid_dict = collections.defaultdict()
uid_dict.default_factory = uid_dict.__len__ # 第1个元组是1,之后依次排列
count = collections.defaultdict(int) # 统计出现次数
res = [] # 保存结果
def lookup(node):
if not node:
return
# uid = (node.val, lookup(node.left), lookup(node.right)) # 唯一的uid
uid = uid_dict[(node.val, lookup(node.left), lookup(node.right))]
count[uid] += 1
if count[uid] == 2:
res.append(node)
return uid
lookup(root)
return res
def main():
root_list = [1, 2, 3, 4, None, 2, 4, None, None, 4]
print("input: ".format(root_list))
root = build_tree(root_list)
print("show: ".format(tree2list(root)))
so = Solution()
res = so.findDuplicateSubtrees(root)
print("res: ".format([tree2list(r) for r in res])) # [[4], [2, 4]]
if __name__ == '__main__':
main()
以上是关于LeetCode - 相同子树替换后最长重复子串多米诺骨牌的主要内容,如果未能解决你的问题,请参考以下文章
[LeetCode] 424. Longest Repeating Character Replacement
Leetcode(无重复字符的最长子串;删除排序链表中的重复元素II;加一;最后一个单词的长度;相同的树)