Acwing799 最长连续不重复子序列

 首先我们要对双指针算法有个基本的认识.

//i作为扫描仪
for(int i=0,j=0;i<n;i++)
     while(j<i&&check(i,j)j++;
//对位于合法范围内的元素i进行某种检验,然后移动它

 双指针算法的作用就在于**它将复杂度为 O ( n 2 ) O(n_2) O(n2)的双重循环算法简化为复杂度为 O ( 2 n ) O(2n) O(2n)的算法。**这一点 可以从上面的代码中看出来,对于i,j,它们各自的使用次数不会超过n。

双指针算法应用:分割单词

给定一个字符串,字符串内包含若干用空格隔开的单词,输出里面的单词。
为了方便说明,这里我们假设开头无空格,单词之间的空格只有一个。

> #include <iostream>
> #include <cstring>//必须是cstring,否则strlen()方法不能用
> #include <stdio.h> using namespace std;
> 
> int main() {
>     char a[40000];
>     gets_s(a);//必须是char型数组,不能是其他类型数组
>     int len = strlen(a);//得到char型数组的实际长度
>     int n = strlen(a);
>     //i代表每个单词的具体开头
>     for (int i = 0; i < n; i++) {
>         int j = i;
>         while (j < n && a[j] != ' ');
> 
>         for (int k = i; k < j; k++)cout << a[k];
>             
>         //输出单词后i指向j,然后下一步i++就会来到新单词的头部
>         i = j;
>     }
>     return 0; }

上面只是最简单的一种情况,接下来我们加大难度。

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

输入格式
第一行包含整数 n。

第二行包含 n 个整数(均在 0105 范围内),表示整数序列。

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

数据范围
1≤n≤105
输入样例:
5
1 2 2 3 5
输出样例:
3

在这里插入图片描述
 对于已有的一段数字序列,我们将i置于j的右侧,让j从i的位置出发,向左移动,探寻最长的非连续整数序列。
 为什么要这么做呢?按照正常的想法,不应该式把 i i i放于 j j j的左侧,然后 j j j向右探测吗?

 下面用图来说明这一原因。
在这里插入图片描述
 我们有以下结论:随着 i i i的向右移动, j j j的位置也将一直向右移动,且每次移动中 j j j的位置永远与上次的位置重合或在上次位置的右边。这点我们可以从 j j j本身代表的意义来理解, j j j的位置是离 i i i最远的非重复整数序列的下标,即 j j j的左边的相邻数字包含着 [ j , i ] [j,i] [j,i]区间中的一个数字,因此无论后面的 i i i去到哪里, j j j绝不会越过上一次自己位置的左边,因为一旦越过左边,在 j i ji ji之间就会出现重复数字,位置为 j j j,与 [ j , i ] [j,i] [j,i]区间中的某一位。

伪代码如下
for(int i=0;i<n;i++)
	 while(j<=i&&check(i,j))j++;
	 res=max(i-j+1,res);

 那么 我们只剩一个问题,就是如何实现这个check函数。

check函数的实现

 基本思想:设置一个计数数组S[N],统计在 [ i , j ] [i,j] [i,j]区间中出现的数字次数,如 1 , 2 , 3 , 3 1,2,3,3 1,2,3,3那么对应的数组元素就是S[1]=1,S[2]=1,S[3]=2。在整个check函数的执行过程中,S[N]中一旦出现某一位元素大于1,就说明出现了重复元素。

代码

#include <iostream>
#include <cstring>//必须是cstring,否则strlen()方法不能用
#include <stdio.h>
using namespace std;
const int N=100010;
int a[N], s[N];
int  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 << endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值