题解:AcWing 799 最长连续不重复子序列

【题目来源】

AcWing:799 最长连续不重复子序列 - AcWing题库

【题目描述】

给定一个长度为 \(n\) 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

【输入】

第一行包含整数 \(n\)

第二行包含 \(n\) 个整数(均在 \(0\sim 10^5\) 范围内),表示整数序列。

【输出】

共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。

【输入样例】

5
1 2 2 3 5

【输出样例】

3

【核心思想】

  1. 问题分析:给定一个长度为 \(n\) 的整数序列,需要找出最长的不包含重复数字的连续子区间(子数组),输出其长度。这是一个双指针(滑动窗口)问题,关键在于如何高效地维护一个不包含重复数字的窗口。

  2. 算法选择

    • 双指针(滑动窗口):使用两个指针 \(i\)(右指针)和 \(j\)(左指针)维护一个窗口 \([j, i]\),窗口内的元素都是不重复的
    • 哈希表/频率数组:用数组 \(s[x]\) 记录数字 \(x\) 在当前窗口中出现的次数,实现 \(O(1)\) 的重复判断
  3. 关键步骤

    • 初始化\(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),记录当前窗口长度
    • 输出最大长度 \(res\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),每个元素最多被左右指针各访问一次
    • 空间复杂度:\(O(n)\)\(O(10^5)\),取决于值域大小,用于频率数组
  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
posted @ 2026-06-14 10:36  团爸讲算法  阅读(11)  评论(0)    收藏  举报