题解:AcWing 799 最长连续不重复子序列
【题目来源】
AcWing:799 最长连续不重复子序列 - AcWing题库
【题目描述】
给定一个长度为 \(n\) 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
【输入】
第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数(均在 \(0\sim 10^5\) 范围内),表示整数序列。
【输出】
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
【输入样例】
5
1 2 2 3 5
【输出样例】
3
【核心思想】
-
问题分析:给定一个长度为 \(n\) 的整数序列,需要找出最长的不包含重复数字的连续子区间(子数组),输出其长度。这是一个双指针(滑动窗口)问题,关键在于如何高效地维护一个不包含重复数字的窗口。
-
算法选择:
- 双指针(滑动窗口):使用两个指针 \(i\)(右指针)和 \(j\)(左指针)维护一个窗口 \([j, i]\),窗口内的元素都是不重复的
- 哈希表/频率数组:用数组 \(s[x]\) 记录数字 \(x\) 在当前窗口中出现的次数,实现 \(O(1)\) 的重复判断
-
关键步骤:
- 初始化:\(j = 0\)(左指针),\(res = 0\)(最大长度),\(s[]\) 全为 \(0\)(频率数组)
- 右指针遍历(\(i\) 从 \(0\) 到 \(n-1\)):
- 将 \(a[i]\) 加入窗口:
s[a[i]]++ - 检查重复:如果
s[a[i]] > 1,说明 \(a[i]\) 在窗口中已存在,需要收缩窗口- 收缩左边界:
while (s[a[i]] > 1),不断右移 \(j\),并将s[a[j]]--,直到s[a[i]] == 1
- 收缩左边界:
- 更新答案:
res = max(res, i - j + 1),记录当前窗口长度
- 将 \(a[i]\) 加入窗口:
- 输出最大长度 \(res\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),每个元素最多被左右指针各访问一次
- 空间复杂度:\(O(n)\) 或 \(O(10^5)\),取决于值域大小,用于频率数组
-
双指针(滑动窗口)的核心思想:
- 单调性:右指针 \(i\) 只向右移动,左指针 \(j\) 也只向右移动,不会回退
- 窗口维护:始终保持窗口 \([j, i]\) 内没有重复元素
- 贪心策略:对于每个右端点 \(i\),找到最远的左端点 \(j\) 使得区间 \([j, i]\) 无重复,这就是以 \(i\) 结尾的最长无重复子数组
- 频率数组优化:用 \(O(1)\) 时间判断元素是否在窗口中,避免 \(O(n)\) 的线性查找
- 适用于子数组/子串问题、区间统计、去重问题等场景
【解题思路】


【算法标签】
双指针
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100010; // 定义数组最大长度
int n; // 数组长度
int a[N]; // 存储原始数组
int s[N]; // 哈希表,记录每个数字出现的次数
int main()
{
// 输入数组长度
cin >> n;
// 输入数组元素
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int res = 0; // 记录最长无重复子数组长度
// 双指针算法:i是右指针,j是左指针
for (int i = 0, j = 0; i < n; i++)
{
s[a[i]]++; // 记录当前数字出现次数
// 如果当前数字出现次数超过1次,移动左指针j
while (s[a[i]] > 1)
{
s[a[j]]--; // 减少左指针指向数字的计数
j++; // 左指针右移
}
// 更新最大长度
res = max(res, i - j + 1);
}
// 输出最长无重复子数组长度
cout << res << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义最大数组长度
int n; // 数组长度
int a[N]; // 存储输入序列
int s[N]; // 频率统计数组,记录每个数字出现的次数
int main()
{
// 输入序列长度
cin >> n;
// 输入序列元素
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
int ans = 0; // 存储最长无重复子串长度
int j = 1; // 滑动窗口左指针
// 使用滑动窗口法遍历序列
for (int i = 1; i <= n; i++)
{
// 将当前数字a[i]加入窗口,增加其出现次数
s[a[i]]++;
// 如果当前数字出现次数不超过1次(即无重复)
if (s[a[i]] <= 1)
{
// 更新最大长度:当前窗口长度 i - j + 1
ans = max(ans, i - j + 1);
}
else
{
// 如果当前数字出现重复(s[a[i]] > 1)
// 移动左指针j,直到消除重复
while (s[a[i]] > 1 && j < i)
{
// 将左指针指向的数字移出窗口
s[a[j]]--;
// 左指针向右移动
j++;
}
}
}
// 输出最长无重复子串的长度
cout << ans << endl;
return 0;
}
【运行结果】
5
1 2 2 3 5
3
浙公网安备 33010602011771号