您的位置:首页 >如何快速找出最长无聊前缀子数组
发布于2026-04-13 阅读(0)
扫一扫,手机访问
本文介绍一种线性扫描结合频次统计的算法,用于在数组中找出满足“移除一个元素后各数字出现次数均相等”的最长前缀长度,时间复杂度 O(n),空间复杂度 O(n)。
本文介绍一种线性扫描结合频次统计的算法,用于在数组中找出满足“移除一个元素后各数字出现次数均相等”的最长前缀长度,时间复杂度 O(n),空间复杂度 O(n)。
“无聊”(boring)前缀的定义是:该前缀中恰好存在一个元素,将其删除后,剩余所有不同数字的出现频次完全相同。例如 [1,2,3,1,2,2,3,3,3,1](长度为10)是无聊的——删去一个 3 后,1、2、3 均出现 3 次。
要高效判断每个前缀 a[0..i] 是否为无聊前缀,关键在于动态维护频次分布的核心统计量,而非对每个前缀重新计数(那将导致 O(n²) 复杂度)。我们需要在遍历过程中实时更新以下四个变量:
这些变量可在每次插入 arr[i] 时 O(1) 更新(通过先减旧频次影响、再加新频次影响实现)。
一个前缀 a[0..i] 是无聊的,当且仅当以下任一条件成立(覆盖所有合法删元情形):
实际编码中,条件2和3可更简洁地用频次映射验证,但上述逻辑已足够指导实现。以下是完整 Java 实现:
import java.util.*;
public class BoringPrefix {
public static int findMaxBoringPrefixLength(int[] arr) {
if (arr == null || arr.length < 2) return 0;
Map<Integer, Integer> freq = new HashMap<>(); // 数字 → 当前频次
int distinct = 0, maxFreq = 0, countMax = 0, countMaxMinusOne = 0;
int result = 0;
for (int i = 0; i < arr.length; i++) {
int x = arr[i];
int oldFreq = freq.getOrDefault(x, 0);
int newFreq = oldFreq + 1;
freq.put(x, newFreq);
// 更新 distinct
if (oldFreq == 0) distinct++;
// 从旧频次组中移除 x 的影响
if (oldFreq > 0) {
if (oldFreq == maxFreq) countMax--;
if (oldFreq == maxFreq - 1) countMaxMinusOne--;
}
// 加入新频次组的影响
if (newFreq > maxFreq) {
// 新频次打破纪录:maxFreq 升级
maxFreq = newFreq;
countMax = 1;
countMaxMinusOne = (oldFreq > 0) ? (oldFreq == maxFreq - 1 ? 1 : 0) : 0;
} else if (newFreq == maxFreq) {
countMax++;
} else if (newFreq == maxFreq - 1) {
countMaxMinusOne++;
}
// 检查是否为 boring 前缀
boolean isBoring = false;
// Case 1: 所有频次为 1
if (maxFreq == 1) {
isBoring = true;
}
// Case 2: 一个元素频次为 maxFreq,其余为 maxFreq-1
else if (countMax == 1 && countMaxMinusOne == distinct - 1) {
isBoring = true;
}
// Case 3: 一个元素频次为 1,其余为 maxFreq(此时 maxFreq >= 2)
else if (countMax == distinct - 1 &&
freq.values().stream().filter(f -> f == 1).count() == 1) {
isBoring = true;
}
if (isBoring) result = i + 1; // 记录最长有效前缀长度
}
return result;
}
// 示例调用
public static void main(String[] args) {
int[] arr = {1, 2, 3, 1, 2, 2, 3, 3, 3, 1, 4, 4, 5};
System.out.println(findMaxBoringPrefixLength(arr)); // 输出: 10
}
}⚠️ 注意事项:
总结:识别“无聊前缀”的本质是识别频次分布的特殊平衡态。通过精心设计的四变量状态机,我们能在 O(n) 时间内完成全部判断,适用于 n ≤ 10⁵ 规模的实际场景。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9