查找最多具有 k 个奇数元素的不同连续子数组的数量

Posted

技术标签:

【中文标题】查找最多具有 k 个奇数元素的不同连续子数组的数量【英文标题】:Find number of distinct contiguous subarrays with at most k odd elements 【发布时间】:2021-11-07 07:39:33 【问题描述】:

给定一个整数数组 nums,找出最多有 k 个奇数元素的不同连续子数组的数量。当两个子数组至少有一个不同的元素时,它们是不同的。

我能够在 O(n^2) 中做到这一点。但需要 O(nlogn) 的解决方案。

示例 1:

Input: nums = [3, 2, 3, 4], k = 1
Output: 7 
Explanation: [3], [2], [4], [3, 2], [2, 3], [3, 4], [2, 3, 4]
Note we did not count [3, 2, 3] since it has more than k odd elements.

示例 2:

Input: nums = [1, 3, 9, 5], k = 2
Output: 7
Explanation: [1], [3], [9], [5], [1, 3], [3, 9], [9, 5]

示例 3:

Input: nums = [3, 2, 3, 2], k = 1
Output: 5
Explanation: [3], [2], [3, 2], [2, 3], [2, 3, 2]
[3], [2], [3, 2] - duplicates
[3, 2, 3], [3, 2, 3, 2] - more than k odd elements

示例 4:

Input: nums = [2, 2, 5, 6, 9, 2, 11, 9, 2, 11, 12], k = 1
Output: 18

【问题讨论】:

【参考方案1】:

我们可以通过两步过程以次二次复杂度解决这个问题。首先使用两个指针勾勒出相关的窗口,我们将使用它们来构建一个通用的后缀树。我们可以通过注意每个重叠将仅插入两次来证明所有窗口的长度为 O(n)。第一个窗口是通过从第一个元素尽可能向右扩展来构建的,我们可以保留一个有效的子数组。通过 (1) 在下一个奇数元素之后扩展左指针,以及 (2) 将右指针扩展至我们可以保留有效子数组的位置,从而创建后续窗口。

Example 1:

3, 2, 3, 2
k = 1

windows: [3 2], [2 3 2]


Example 2:

1, 2, 2, 2, 3, 4, 4, 5, 5
k = 2

windows:
[1 2 2 2 3 4 4], [2 2 2 3 4 4 5], [4 4 5 5] 

构建一个通用的后缀树。不同子集的计数将等于存储在树中的后缀的累积长度之和。 (“累积长度”是指:例如,给定后缀“abc”,我们将添加 1 + 2 + 3,每次从后缀的开头延伸得更远。或者通过公式 n * (n + 1) / 2 )

正如 kcsquared 指出的 in the comments,不需要通用后缀树。相反,我们可以使用一种已知的方法来“计算具有后缀数组和最长公共前缀数组的不同子字符串的总数,但不是对 n - suffix_array_elements 求和,...将 n 替换为该索引的最大右边界。”

【讨论】:

你能解释一下'概述相关窗口'和'所有窗口加在一起是O(n)'的意思吗?你指的是哪个窗口?似乎也没有必要建立一个通用的后缀树而不是一个常规的,尽管我可能基于之前关于 windows 的评论误解了这一点。从每个位置开始的所有最大有效子数组的总长度可以是二次的,因此您必须以其他方式处理重叠。 你能告诉我们为 k=n 构建后缀树的复杂性是什么吗? @VladimirNesterovsky 如果 k == n,则只有一个窗口。 @kcsquared 我在答案中给出了一个窗口的例子(元素是空格分隔的,窗口是逗号分隔的)。树处理重叠,但我很高兴听到如何在输入上使用后缀数组来做到这一点。 @גלעדברקן 我不确定我首先了解窗口是如何定义的。仅使用后缀数组 (Idea taken from this C++ implementation) 执行此操作与使用后缀数组和最长公共前缀数组计算不同子字符串的总数完全相同,但不是对 n - suffix_array_elements 求和,而是将 n 替换为最大值该索引的右边界。这在链接代码中可能更清楚。【参考方案2】:

考虑一个完全由奇数元素组成的数组。

结果子数组的数量是n*k。 如果k 等于n,那么子数组的数量就是~n*n

所以,您想使用 O(nlogn) 操作找到 ~n*n 子数组。

我怀疑是否存在具有要求的复杂性的算法。

【讨论】:

对于这个问题存在一个 O(n log n) 算法是非常令人惊讶的;但这也是正确的,并且有几种使用后缀数组的实现。使用 Farach 的后缀树算法,如果 nums 的元素在量级上至多是多项式,则存在 O(n) 解。事实上,由于我们不需要这个问题的确切后缀树,因此也可能有一个无条件的 O(n) 解决方案。将问题标题的确切文本放入 Google 会显示 Leetcode 帖子,其中包含 n log n C++ 代码。 这并不能证明复杂度低于O(n^2)的算法不存在。 我没有告诉我有证据,但表示怀疑。我接受我的知识差距,并且已经研究过可以将后缀树构建为 n*logn 甚至线性复杂性任务。可以使用线性复杂度查询此树以找到请求的答案。【参考方案3】:

如果我们只需要输出子数组的数量,那么我相信它可以在 O(n) 时间复杂度中使用两个指针方法 + 滑动窗口来完成。

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于查找最多具有 k 个奇数元素的不同连续子数组的数量的主要内容,如果未能解决你的问题,请参考以下文章

最大连续子数组(元素数量最多)

最多有多少个连续子数组。 n 个唯一编号

c#查找最长连续子数组第一个和最后一个元素索引

[虚拟机OA]Even Subarray 最多含有K个奇数的子数组

程序将数组分成N个连续子数组,使每个子数组的和为奇数

从向量数组中查找给定向量中公共元素的数量