双指针算法-最长连续不重复子序列
给定一个长度为n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
例如:
输出:
输入:
7
1 2 3 2 4 5 6
输出:5
方法一:穷举法
对每个 i 和 j 都遍历一遍,对每个 i 和 j 都check一下中间的数据是否满足给定的条件。这样的时间复杂度是O(n^2);数据稍微大点就会超时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <iostream> using namespace std; int arr[100]; int check(int arr[], int l, int r){ for (int i = l+1; i <=r ; i++) for (int j = l; j < i; j++) if (arr[i] == arr[j]) return 1; return 0; } int main(){ int n,res=0; cin>>n; for (int i = 0; i <n; i++)cin>>arr[i]; for (int i = 0; i < n; i++) for (int j = 0; j <= i; j++) if (check(arr,j, i) == 0)//检查 i 和 j 之间是否有重复的数字 res = max(res, i - j + 1); cout<<res; } |
方法二:双指针法
遍历数组a中的每一个元素a[i], 对于每一个i,找到j使得双指针[j, i]维护的是以a[i]结尾的最长连续不重复子序列,长度为i – j + 1, 将这一长度与r的较大者更新给r。
对于每一个i,如何确定j的位置:由于[j, i – 1]是前一步得到的最长连续不重复子序列,所以如果[j, i]中有重复元素,一定是a[i],因此右移j直到a[i]不重复为止(由于[j, i – 1]已经是前一步的最优解,此时j只可能右移以剔除重复元素a[i],不可能左移增加元素,因此,j具有“单调性”、本题可用双指针降低复杂度)。
用数组s记录子序列a[j ~ i]中各元素出现次数,遍历过程中对于每一个i有四步操作:
第一步:cin元素a[i] 。
第二步:将a[i]出现次数s[a[i]]加1
第三步:若a[i]重复(>1) 则右移j(s[a[j]]要减1) 确定j及更新当前长度i – j + 1给ans。
第四步:输出最大的ans。
代码实现-数组计数:
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 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2022-07-29 07:18 * 最后修改: 2026-05-11 12:39:39 * 文件描述: 最长不重复数字子串,滑动窗口解法,时间复杂度 O(n) * 核心算法: 双指针维护无重复窗口,右指针扩张、重复时左指针收缩; * 本写法用数组计数,要求数字非负且值域不能过大 ****************************************************************/ #include <algorithm> #include <iostream> using namespace std; const int N = 100010; int a[N], s[N]; int main() { // ios::sync_with_stdio(false); // cin.tie(nullptr); int n, ans = 0; cin >> n; for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 0, j = 0; i < n; i++) { // 先把 a[i] 放入当前窗口,并统计它在窗口中的出现次数。 s[a[i]]++; // 如果 a[i] 出现重复,就不断移动左端点 j,直到窗口重新合法。 while (s[a[i]] > 1) { s[a[j]]--; j++; } // 此时区间 [j, i] 内所有数字都互不相同,可以更新答案。 ans = max(ans, i - j + 1); } cout << ans; return 0; } |
代码实现-哈希表:
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 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2026-05-11 12:38 * 最后修改: 2026-05-11 12:44:06 * 文件描述: 最长不重复数字子串,unordered_map 版滑动窗口解法,时间复杂度 O(n) * 核心算法: 双指针维护无重复窗口,右指针扩张、重复时左指针收缩; * 用无序映射(哈希表)计数,可处理负数或大值域数字 ****************************************************************/ #include <algorithm> #include <iostream> #include <unordered_map> using namespace std; const int N = 100010; int a[N]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, ans = 0; cin >> n; for (int i = 0; i < n; i++) { cin >> a[i]; } unordered_map<int, int> cnt; for (int i = 0, j = 0; i < n; i++) { // 把 a[i] 放入窗口,并记录它在当前窗口中的出现次数。 cnt[a[i]]++; // 如果 a[i] 重复,就不断收缩左端点,直到窗口恢复为无重复状态。 while (cnt[a[i]] > 1) { cnt[a[j]]--; j++; } // 窗口合法后,用当前窗口长度更新答案。 ans = max(ans, i - j + 1); } cout << ans; return 0; } |
洛谷:U224090,T650836
OJ: Y3442, Y3443
