题解:学而思编程 最长平台(二)
【题目来源】
洛谷:最长平台(二)
【题目描述】
已知一个数列,这个数列中的一个平台就是连续的一串值相同的元素,并且这一串元素不能再延伸。例如,在 \(1,2,2,3,3,3,4,5,5\) 中的 \([1]\),\([2,2]\),\([3,3,3]\),\([4]\),\([5,5]\) 都是平台,最长平台的长度为 \(3\)。
给定一个整数数列 \(a_1,a_2,…,a_n\),请找出最长平台,并输出最长平台的数量。
【输入】
第一行,包含一个正整数 \(n\),表示数列长度
第二行,包含 \(n\) 个正整数 \(a_1,a_2,…,a_n\),表示数列,用空格隔开。
【输出】
一行包含两个整数:表示最长平台的长度与最长平台的数量。
【输入样例】
7
2 2 2 1 3 3 3
【输出样例】
3 2
【核心思想】
-
问题分析:给定长度为 \(n\) 的数列 \(a_1, a_2, \ldots, a_n\),平台定义为无法再向左右延伸的连续相同元素段。需要找出最长平台的长度以及该长度的平台数量。这是一个线性扫描 + 哈希统计问题,关键在于一次遍历识别所有平台,并用映射表记录各长度出现的频次。
-
算法选择:
- 线性扫描:遍历数组,维护当前连续相同元素的长度
cnt - 哈希映射:用
map记录每种平台长度出现的次数 - 极值提取:遍历映射表找出最大长度及其频次
- 线性扫描:遍历数组,维护当前连续相同元素的长度
-
关键步骤:
- 初始化:读取 \(n\) 和数组 \(a[1..n]\)
- 扫描平台(遍历 \(i\) 从 \(1\) 到 \(n\)):
- 若
a[i] == a[i-1]:cnt++(当前平台长度加 \(1\)) - 否则:
mp[cnt]++(记录前一个平台的长度),cnt = 1(重置计数,新平台从当前元素开始)
- 若
- 处理末尾:循环结束后,
mp[cnt]++(记录最后一个平台的长度) - 提取答案:
- 遍历
mp中所有(长度, 频次)对:- 若当前长度
> maxn:更新maxn和maxcnt
- 若当前长度
- 遍历
- 输出
maxn和maxcnt
-
时间/空间复杂度:
- 时间复杂度:\(O(n + U)\),\(U\) 为不同平台长度的种类数(\(U \leq n\)),线性扫描 \(O(n)\),映射表遍历 \(O(U)\)
- 空间复杂度:\(O(U)\),映射表存储各长度频次
-
线性扫描的核心思想:
- 平台识别:相邻元素相等则延长当前平台,不等则结束当前平台并开始新平台,保证每个平台只被计数一次
- 边界处理:数组首元素作为第一个平台的起点,末尾平台在循环结束后单独处理,避免遗漏
- 频次统计:
map自动聚合相同长度的平台,无需预先知道长度范围 - 极值提取:映射表按键有序,但本题只需找最大值,线性遍历即可
- 适用于"连续段识别 + 段长度统计"类问题,线性扫描配合哈希映射可高效处理所有连续子段信息
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 200005; // 定义最大数组长度
int n; // 数组长度
int a[N]; // 数组a
map<int, int> mp; // 记录长度和频次的映射表
int main() // 主函数
{
cin >> n; // 输入数组长度
for (int i=1; i<=n; i++) // 读取数组元素
cin >> a[i]; // 输入第i个元素
int cnt = 1; // 当前连续相同序列长度
for (int i=1; i<=n; i++) // 遍历数组
if (a[i]==a[i-1]) // 如果当前元素等于前一个元素
{
cnt++; // 连续长度加1
}
else // 如果不相等
{
mp[cnt]++; // 记录前一个连续序列的长度
cnt = 1; // 重置连续长度计数
}
if (a[n]==a[n-1]) mp[cnt]++; // 处理最后一个连续序列
int maxn = -1e9, maxcnt = 0; // 最大长度和其出现次数
for (auto it : mp) // 遍历映射表
{
if (it.first>maxn) // 如果当前长度更大
{
maxn = it.first; // 更新最大长度
maxcnt = it.second; // 更新出现次数
}
}
cout << maxn << " " << maxcnt << endl; // 输出结果
return 0; // 程序正常结束
}
【运行结果】
7
2 2 2 1 3 3 3
3 2
浙公网安备 33010602011771号