题解:P12280 [蓝桥杯 2024 国 Python A] 特别的数组
思路与算法
主思路与算法
首先,题目要求的是移除一个区间后剩下的元素必须全部唯一。那么最大的剩余长度就是原长度减去被移除区间的长度。所以,问题转化为找到最小的需要移除的区间长度,这样剩下的部分就最长。因此,我们的目标应该是找到这样的一个最小的区间,当移除它之后,剩下的元素都是唯一的。
那怎么找到这个最小的区间呢?直接想到的是,如果我们能确定哪些元素是重复的,那么我们需要移除的区间必须覆盖所有重复元素中的一个或多个,使得剩下的元素不再重复。但这似乎比较复杂,因为可能有多个重复元素分布在数组的不同位置。
所以,我们思考,如何高效地找到这个最小的移除区间呢?直接暴力枚举所有可能的L和R显然不可行,因为时间复杂度会是 \(O(n^2)\),对于 \(n = 1 \times 10^5\) 的情况,这完全无法处理。
这时候,想到滑动窗口(双指针)的方法。滑动窗口通常用来处理子数组或子区间的问题,尤其是涉及连续元素的情况。那么,这里的问题是否可以转化为滑动窗口的问题?
滑动窗口的核心思想是维护一个窗口,使得窗口内的元素满足某种条件,而窗口外的元素满足另一种条件。例如,在这个问题中,我们的目标是找到一个窗口 \([L, R]\),当移除这个窗口后,剩下的元素都是唯一的。也就是说,窗口外的元素必须全部唯一。
换句话说,我们需要找到一个窗口,使得在数组的左右两部分(窗口左边和窗口右边)中的元素都没有重复。或者更准确地说,整个数组除了窗口内的元素之外,剩下的元素都是唯一的。
这时候,问题转化为寻找一个窗口,使得窗口外的元素都不重复。找到这样的窗口后,我们要找出其中长度最小的那个窗口,这样原数组长度减去这个窗口长度就是最大的剩余长度。
于是,滑动窗口的思路是可行的。具体来说,我们需要从数组中移除一个连续区间 \([L, R]\),使得剩余元素全为唯一,且剩余部分尽可能长。目标转化为寻找移除的最小区间长度,使得剩余元素唯一。
这个思路的关键点在于,如何高效地维护窗口外元素的出现次数,并快速判断是否满足条件。使用滑动窗口可以在 \(O(n)\) 的时间复杂度内完成,因为每个元素最多被左右指针各遍历一次(计算过程中)。
副算法
- 哈希表模拟:通过数组 \(cnt\) 直接索引元素值,统计频率,避免复杂数据结构。
代码
首先统计整个数组中每个元素的出现次数。如果原数组已经是所有元素唯一的(特殊情况),那么不需要移除任何区间,直接返回 \(n\)。否则,我们需要找到最小的窗口 \([L, R]\),使得移除这个窗口后剩下的元素唯一。
接着,使用滑动窗口的方法,维护左右指针,以及窗口外的元素的频率表。我们这里命名一个变量 \(over-cnt\),表示窗口外有多少个元素的出现次数超过 \(1\)。右指针不断右移,将当前元素移出窗口外(即其频率减 \(1\))。如果这个操作导致该元素的频率从 \(2\) 变为 \(1\) ,那么 \(over_cnt\) 减 \(1\),因为该元素在窗口外不再重复。
当 \(over-cnt\) 变为 \(0\) 时,说明窗口外的元素没有重复。此时,尝试收缩左指针,移动左指针,将元素重新加入窗口外(即其频率加 \(1\))。如果某个元素的频率从 \(1\) 变为 \(2\),则 \(over-cnt\) 加 \(1\),此时停止收缩,记录当前窗口的长度是否为最小值。
最终,找到最小的窗口长度,原数组长度减去这个长度就是答案。
我们需要从数组中移除一个连续区间 \([L, R]\),使得剩余元素全为唯一,且剩余部分尽可能长。目标转化为寻找移除的最小区间长度,使得剩余元素唯一。
最后献上我的几篇代码:
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 41 42 43 | |
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 41 42 43 44 45 46 47 48 | |
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 41 42 | |
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 | |