LeetCode 双周赛 107(2023/06/24)滑动窗口与离散化
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问。
T1. 最大字符串配对数目(Easy)
- 标签:散列表
T2. 构造最长的新字符串(Medium)
- 标签:模拟
T3. 字符串连接删减字母(Medium)
- 标签:状态 DP
T4. 统计没有收到请求的服务器数目(Medium)
- 标签:排序、滑动窗口、离散化

T1. 最大字符串配对数目(Easy)
https://leetcode.cn/problems/find-maximum-number-of-string-pairs/
题解(散列表)
题目说明所有字符串不相同,因此我们可以枚举每个字符串,检查其反转是否存在,模板类似于两数之和;
- 扩展:如果字符串存在重复,可以将配对的字符分组再按两两配对计算;
- 扩展:如果字符串长度很长会存在散列冲突,可以调整 U 为较大素数。
class Solution {
fun maximumNumberOfStringPairs(words: Array<String>): Int {
val U = 26
val set = HashSet<Int>()
var ret = 0
for (word in words) {
if (word.length != 2) continue
val key = (word[0] - 'a') * U + (word[1] - 'a')
val reversedKey = (word[1] - 'a') * U + (word[0] - 'a')
if (set.contains(reversedKey)) ret ++
set.add(key)
}
return ret
}
}
复杂度分析:
- 时间复杂度:$O(L)$ 需要访问每个单词的每个字符;
- 空间复杂度:$O(n)$ 散列表空间。
T2. 构造最长的新字符串(Medium)
https://leetcode.cn/problems/construct-the-longest-new-string/
题解(模拟)
根据题意分析,我们总可以将 ABAB..ABAB 置于结果中间,再将 AA 或 BB 置于两边。此时所有 AB 都可选,而 AA BB 最多只能选择较少者 + 1,分类讨论即可:
class Solution {
fun longestString(x: Int, y: Int, z: Int): Int {
return if (x == y) {
(x + y + z) * 2
} else {
(Math.min(x, y) * 2 + 1 + z) * 2
}
}
}
复杂度分析:
- 时间复杂度:$O(1)$
- 空间复杂度:$O(1)$
T3. 字符串连接删减字母(Medium)
https://leetcode.cn/problems/decremental-string-concatenation/
题解(状态 DP)
- 对于每个字符串 [i],有拼接到前方或拼接到后方两种方案;
- 当考虑 join(i, i + 1) 时,我们只需要关心两个字符串的首尾 4 个字符,对于中间的字符是不关心的。因此在遍历到字符串 [i] 时,我们检查以 [i - 1] 为结尾的子问题的可能方案,并维护以 [i] 为结尾的子问题的所有方案。
class Solution {
fun minimizeConcatenatedLength(words: Array<String>): Int {
val INF = 0x3F3F3F3F
val n = words.size
val dp = Array(n) { Array(26) { IntArray(26) { INF } }}
// 起始状态
dp[0][words[0][0] - 'a'][words[0][words[0].length - 1] - 'a'] = words[0].length
for (i in 1 until n) {
val word = words[i]
val x = word[0] - 'a'
val y = word[word.length - 1] - 'a'
// 枚举子问题状态
for (j in 0 until 26) {
for (k in 0 until 26) {
// 拼接到前方
if (y == j) {
dp[i][x][k] = Math.min(dp[i][x][k], dp[i - 1][j][k] + word.length - 1)
} else {
dp[i][x][k] = Math.min(dp[i][x][k], dp[i - 1][j][k] + word.length)
}
// 拼接到后方
if (x == k) {
dp[i][j][y] = Math.min(dp[i][j][y], dp[i - 1][j][k] + word.length - 1)
} else {
dp[i][j][y] = Math.min(dp[i][j][y], dp[i - 1][j][k] + word.length)
}
}
}
}
var ret = INF
for (j in 0 until 26) {
for (k in 0 until 26) {
ret = Math.min(ret, dp[n - 1][j][k])
}
}
return ret
}
}
复杂度分析:
- 时间复杂度:$O(n·C^2)$ C= 26
- 空间复杂度:$O(n·C^2)$
T4. 统计没有收到请求的服务器数目(Medium)
https://leetcode.cn/problems/count-zero-request-servers/
题解一(暴力)
线性扫描日志,并线性扫描查询列表,将日志记录投递到对应的查询中,同时使用散列表对相同服务器去重。
class Solution {
fun countServers(n: Int, logs: Array<IntArray>, x: Int, queries: IntArray): IntArray {
val m = queries.size
val sets = Array(m) { HashSet<Int>() }
val ret = IntArray(m)
// 暴力
for (log in logs) {
for ((i, query) in queries.withIndex()) {
if (log[1] in query - x .. query) {
sets[i].add(log[0])
}
}
}
// 输出
for (i in 0 until m) {
ret[i] = n - sets[i].size
}
return ret
}
}
复杂度分析:
- 时间复杂度:$O(nm)$ 超出时间限制;
- 空间复杂度:$O(nm)$ 散列表空间,最坏情况下每个查询中包含所有服务器记录。
题解二(排序 + 滑动窗口 + 离散化)
需要注意题目中的单调性,对于日志时间 log_i < log_j,当 log_i 时间晚于 query_k 时,那么日志时间更晚的 log_k 必然无法投递到 query_k 中,而暴力算法中没有利用单调性质。因此,我们先对 log 日志列表和 queries 查询列表按时间顺序排序,再来使用滑动窗口来维护每个查询中覆盖的日志信息。
class Solution {
fun countServers(n: Int, logs: Array<IntArray>, x: Int, queries: IntArray): IntArray {
val l = logs.size
val m = queries.size
val ret = IntArray(m)
// 查询索引
val indexs = Array(m) { it }
// 排序
Arrays.sort(logs) { i1, i2 ->
i1[1] - i2[1]
}
Arrays.sort(indexs) { i1, i2 ->
queries[i1] - queries[i2]
}
// 滑动窗口 + 离散化
var i = 0
var j = 0
val cnts = HashMap<Int, Int>()
for (id in indexs) {
val to = queries[id]
if (to <= x) throw IllegalStateException()
// 拓展右指针
while (j < l && logs[j][1] <= to) {
cnts[logs[j][0]] = cnts.getOrDefault(logs[j][0], 0) + 1
j++
}
// 收缩左指针
while (i < l && logs[i][1] < to - x) {
cnts[logs[i][0]] = cnts[logs[i][0]]!! - 1
if (cnts[logs[i][0]]!! == 0) cnts.remove(logs[i][0])
i++
}
ret[id] = n - cnts.size
}
return ret
}
}
复杂度分析:
- 时间复杂度:$O(mlgm + llogl + m + l)$ 瓶颈在排序;
- 空间复杂度:$O(m + n)$ 查询索引数组空间 + 散列表空间。
往期回顾
- LeetCode 单周赛第 348 场 · 数位 DP 模版学会了吗?
- LeetCode 单周赛第 347 场 · 二维空间上的 LIS 最长递增子序列问题
- LeetCode 双周赛第 104 场 · 流水的动态规划,铁打的结构化思考
- LeetCode 双周赛第 103 场 · 区间求和的树状数组经典应用

LeetCode 双周赛 107(2023/06/24)滑动窗口与离散化的更多相关文章
- LeetCode 双周赛 102,模拟 / BFS / Dijkstra / Floyd
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,欢迎来到小彭的 LeetCode 周赛解题报告. 昨晚是 LeetCode 双周赛第 102 场,你 ...
- LeetCode 双周赛 98,脑筋急转弯转不过来!
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 昨晚是 LeetCode 第 98 场双周赛,你参加了吗?这场周赛需要脑筋急转弯,转不过 ...
- 刷爆 LeetCode 双周赛 100,单方面宣布第一题最难
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 100 场双周赛,你参加了吗?这场周赛整体没有 Hard ...
- LeetCode 双周赛 101,DP/中心位贪心/裴蜀定理/Dijkstra/最小环
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 这周比较忙,上周末的双周赛题解现在才更新,虽迟但到哈.上周末这场是 LeetCode 第 ...
- [leetcode]346. Moving Average from Data Stream滑动窗口平均值
Given a stream of integers and a window size, calculate the moving average of all integers in the sl ...
- leetcode 3 Longest Substring Without Repeating Characters(滑动窗口)
用滑动窗口的思想来做.用一个unordered_map来查询之前的char有没有在现在的窗口中. class Solution { public: int lengthOfLongestSubstri ...
- Leetcode 30 串联所有单词的子串 滑动窗口+map
见注释.滑动窗口还是好用. class Solution { public: vector<int> findSubstring(string s, vector<string> ...
- [每日一题2020.06.16] leetcode双周赛T3 5423 找两个和为目标值且不重叠的子数组 DP, 前缀和
题目链接 给你一个整数数组 arr 和一个整数值 target . 请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target .可能会有多种方案,请你返回满足要求的两个子数组长度和的 ...
- LeetCode双周赛#36
1604. 警告一小时内使用相同员工卡大于等于三次的人 题目链接 题意 给定两个字符串数组keyName和keyTime,分别表示名字为keytime[i]的人,在某一天内使用员工卡的时间(格式为24 ...
- leetcode 双周赛9 进击的骑士
一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上,你的 骑士 驻扎在坐标为 [0, 0] 的方格里. 骑士的走法和中国象棋中的马相似,走 “日” 字:即先向左(或右 ...
随机推荐
- [数据库/MySQL]数据类型:enum 枚举类型
1 需求描述 场景 性别(gender) :男 / 女 / 保密 2 基本语法 enum(枚举值 1,枚举值 2...); 枚举值列表在 255 个以内,使用 1 个字节来存储 枚举值列表超过 255 ...
- HTTP.sys漏洞的检测和修复(附补丁包下载)
关于这个 HTTP.sys 漏洞,查了一些资料,没有一个写的比较全的,下面我来整理下. 这个漏洞主要存在Windows+IIS的环境下,任何安装了微软IIS 6.0以上的Windows Server ...
- day15:递归函数&递归练习题
递归函数 递归函数的定义 : 自己调用自己的函数就是递归 递: 去 归: 回 一去一回就是递归 一个简单的递归例子 # 1.一个简单的递归例子 def digui(n): print(n,& ...
- DBA面试小结
问题描述:一个DBA在面试过程中,面试官最喜欢提问哪些问题,经过这些天的面试经历,总结了一些面试经验. 普通的外包可能只有一轮技术面试再加一轮人资面试,外包项目一般急需用人,所以面试流程基本简化,合适 ...
- JSON.parse 函数 (JavaScript)
将 JavaScript 对象表示法 (JSON) 字符串转换为对象. 语法 参数 返回值 异常 以下示例使用 JSON.parse 将 JSON 字符串转换成对象. var jsontext = ' ...
- 关于react的Tabs组件中TabPane的bug
今天解决了我自认为一个很不起眼的Bug. 我的Tabs下面有5个tabPane,并且这几个tabPane共用了一个search组件,今天遇到了一个bug,就是这几个组件使用公共查找组件的时候,前一个组 ...
- MySQL 中常见的几种高可用架构部署方案
MySQL 中的集群部署方案 前言 MySQL Replication InnoDB Cluster InnoDB ClusterSet InnoDB ReplicaSet MMM MHA Galer ...
- C51笔记-郭天祥-第二章 从点灯大师开始
第2章 Keil软件的使用及流水灯设计 Keil的用法:用Keil建立工程: 工程配置: C51单片机程序软件仿真.单步.全速.断点设置和变量查看等: 用一个完整的C51程序操控LED亮灭: 调用库 ...
- 2022-07-27:小红拿到了一个长度为N的数组arr,她准备只进行一次修改, 可以将数组中任意一个数arr[i],修改为不大于P的正数(修改后的数必须和原数不同), 并使得所有数之和为X的倍数。
2022-07-27:小红拿到了一个长度为N的数组arr,她准备只进行一次修改, 可以将数组中任意一个数arr[i],修改为不大于P的正数(修改后的数必须和原数不同), 并使得所有数之和为X的倍数. ...
- 2021-12-30:分裂问题。 一个数n,可以分裂成一个数组[n/2, n%2, n/2], 这个数组中哪个数不是1或者0,就继续分裂下去。 比如 n = 5,一开始分裂成[2, 1, 2], [2
2021-12-30:分裂问题. 一个数n,可以分裂成一个数组[n/2, n%2, n/2], 这个数组中哪个数不是1或者0,就继续分裂下去. 比如 n = 5,一开始分裂成[2, 1, 2], [2 ...