双指针【算法推导、背模板】——最长连续不重复子序列

文章详细介绍了如何运用双指针算法解决AcWing题库中的一个问题,即找到数组中的最长连续且不重复的子序列。通过使用一个指针遍历数组,另一个指针维护最长不重复子序列的结束位置,结合哈希表判断元素是否重复,最终实现线性时间复杂度的解法。在过程中,文章强调了双指针移动的逻辑和避免重复的关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

通常情况双指针就是需要将O(N^2^),利用某些单调性质实现O(N)

通用代码模板

for(int i = 0 , j = 0; i < n ; i ++){
	while(j < i && check(i , j ) ) j ++;

	// 需要处理的逻辑
}

check判断是否构成

算法推导

题目中要求我们求

  • 最长的
  • 连续的
  • 不重复子序列

问题一:为什么能想到使用双指针算法?

双指针算法适用于多种场景,其中之一就是在一个序列中寻找一个连续的空间

在本题中,我们可以使用指针i遍历整个序列,使用另一个指针j维护以i为右端点——最长的不重复的子序列。

既然知道这题我们需要使用 双指针算法 寻找一个连续子序列,所以我们就可以带入模板

确定判断条件

带入模板后,我们需要确定在什么样的情况下,j++——也就是想清楚,check函数的逻辑

我们重新审题,发现题目一共有三个条件:最长连续不重复

使用双指针算法一定能让其连续,最长也可以通过设置比较res 和 (i到j的距离)得到最大值

所以这里的check函数的逻辑就是得到一个 不重复 的子序列!

问题二:如何判断该值是否在数组中出现过一次,是否重复?

这里我们想到的就是使用hash表进行判断,也就是在直接以输入的值作为key,每次遇到一次,对应的下标就+1

翻译成代码就是

s[ a[i] ] ++;

所以后续若我们需要判断是否重复过,只需要判断对应的a[i]是否大于1

我们来对输入样例数据1 2 2 3 5 来模拟一遍。

开始时,指针i和指针j同时指向第一个元素

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T8qdJiHq-1682000034349)(assets/image-20230420214905-8d8drb6.png)]

对应数组S的变化情况
> [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0dh4rp2-1682000034351)(assets/image-20230420215112-jj5ulqn.png)]

判断当前s[a[i] ] = 1,不大于1

指针i指向下一个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zoVB0YtZ-1682000034351)(assets/image-20230420215140-u5kbdcq.png)]

对应数组S的变化情况
> [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ib62nvXE-1682000034351)(assets/image-20230420215159-qax3plm.png)]

判断当前s[a[i] ] = 1,不大于1

i继续向后走

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cO2Y2Waz-1682000034352)(assets/image-20230420215301-ygedlzp.png)]

对应数组S的变化情况
在这里插入图片描述

判断当前s[a[i] ] = 2,大于1

所以此时需要将j++,对应记录的数组S就需要–了,因为子序列缩减了

因为数组S表示对应a[i]出现的次数

引申一下,就可以得到子序列的长度(因为子序列在遇到重复元素时是需要缩减的!)

指针J指向下标为1的位置,对应a[i] = 1的值减小

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9miG1yI-1682000034352)(assets/image-20230420215849-vuyo5n3.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bN7qppsL-1682000034352)(assets/image-20230420215858-zax2f3t.png)]

j继续向后走,对应值继续缩减

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gJNozzPF-1682000034353)(assets/image-20230420220429-qwgk3dz.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4FlOcEs-1682000034353)(assets/image-20230420220436-tdzcy07.png)]

此时对应的数组S的值就不再>1,退出循环

此后就一直执行该判断,最后得到的结果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDLMqpnU-1682000034353)(assets/image-20230420220620-bd065s4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kochIeyq-1682000034354)(assets/image-20230420220625-xw1eyut.png)]

所以最后得到的最长子序列长度就是3了,对应计算就呼之欲出了res = max(res , i - j + 1)

表示当前res 和 i和j之间的距离的最大值

问题三:为什么这里要用循环进行判断,而不是直接使用if,然后将j = i ,不是更好吗?

第一点:我们是使用的是双指针算法,算法模板中就是while循环

第二点:我们需要一个一个的消除值,因为我们的区间在不断缩减。如果我们直接将j = i,其中a[i]值前面的S数组的值就不会被清空,在后续遇到时,可能非法触发!!!

例如这个例子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SGEqXiKz-1682000034354)(assets/image-20230420221048-rww8psh.png)]

这里找到的最长子序列是[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFdGylDl-1682000034354)(assets/image-20230420221128-adyso8o.png)]

但是实际序列是[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55hReJPG-1682000034354)(assets/image-20230420221141-t7e2aw3.png)]

因为这里39都没有被清空,所以再一次遇到对应的s [ a[i] ]的值就会大于1,修改j的值

第三点:一般情况我们也是采用循环而不是判断,因为可能出现不同的情况,特别是对于这种前面的状态会影响后面的状态

解答代码

#include<iostream>

using namespace std;

const int N = 1e5+10;

int n , a[N] , s[N];

int main(){
    cin >> n;
  
    for(int i = 0 ; i < n ; i ++){
        cin >> a[i];
    }
  
    int res = 0;
  
    for(int i = 0 , j = 0 ; i < n ; i ++){
        s[a[i]] ++;
      
        while(s[a[i]] > 1){
            s[a[j]]--;
            j ++;
        }
      
        res = max(res , i - j + 1);
      
    }
    cout << res;
  
  
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值