其他分享
首页 > 其他分享> > 给定一个长度为 n 的非负整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

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

作者:互联网

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

一般做法:

  1. 对于 i ∈ [ 0 ,    n    −    1 ] i\in\lbrack0,\;n\;-\;1\rbrack i∈[0,n−1]) ,找到整数序列中以 i i i为区间右端点的最长的不包含重复元素的连续区间,得到其长度 k i k_i ki​。
  2. k    =    m a x { k i    ∣    i ∈ [ 0 ,    n − 1 ]    } k\;=\;max\{k_{i\;}\vert\;i\in\lbrack0,\;n-1\rbrack\;\} k=max{ki​∣i∈[0,n−1]}

简化型代码

int k = 0;
for(int i = 0; i < n; i++){
	for(int j = 0; j < i; j++){
		if(!check(j, i)){	//check(j, i)是检查区间[j, i]内是否存在重复元素,如果存在,则返回true, 否则,返回false。
			k = max(k, i - j + 1);	//k为最大长度
			break;
		}
	}
}

改良版

上述代码实现时出现的问题:
当 i i i 改变了, j j j 的值要从0开始遍历,这一点大可不必。观察下图:
在这里插入图片描述
假如,此时区间 [ j , i ] \lbrack j,i\rbrack [j,i]之间出现了重复元素 a [ m ] = a [ n ] a\lbrack m\rbrack=a\lbrack n\rbrack a[m]=a[n],那么下次当 i i i 移动后, j j j 其实没有必要从0开始,而至少是从 m m m 之后, i i i 之前的某个位置开始。

解决思路

  1. 在计算 k i k_i ki​ 时,在任意时刻, [ j ,    i ] \lbrack j,\;i\rbrack [j,i] 区间只存在两种情况:
    c h e c k ( j , i ) = { t r u e            区 间 [ j , i ] 包 含 重 复 元 素 f a l s e        区 间 [ j , i ] 不 包 含 重 复 元 素 check(j,i)=\left\{\begin{array}{l}true\;\;\;\;\;\mathrm{区间}\lbrack j,i\rbrack\mathrm{包含重复元素}\\false\;\;\;\mathrm{区间}\lbrack j,i\rbrack\mathrm{不包含重复元素}\end{array}\right. check(j,i)={true区间[j,i]包含重复元素false区间[j,i]不包含重复元素​
    (1)当 c h e c k ( j , i ) = f l a s e check(j,i)=flase check(j,i)=flase 时, k i = i − j + 1 k_i=i-j+1 ki​=i−j+1 ;
    (2)当 c h e c k ( j , i ) = t r u e check(j,i)=true check(j,i)=true 时,区间 [ j ,    i ] \lbrack j,\;i\rbrack [j,i] 包含重复元素,假定重复元素为 a [ m ] = a [ n ] a\lbrack m\rbrack=a\lbrack n\rbrack a[m]=a[n],则慢慢移动 j j j,直到 c h e c k ( j , i ) = f a l s e check(j,i)=false check(j,i)=false 为止,此时 j = m j=m j=m, k i = i − j + 1 k_i=i-j+1 ki​=i−j+1。

在这里插入图片描述

  1. 移动 i i i , j j j 从 m m m 开始,重复第一步骤
  2. k    =    m a x { k i    ∣    i ∈ [ 0 ,    n − 1 ]    } k\;=\;max\{k_{i\;}\vert\;i\in\lbrack0,\;n-1\rbrack\;\} k=max{ki​∣i∈[0,n−1]}

改良版简化代码

int k = 0;
for(int i = 0, j = 0; i < n; i++){
	while(j < i && check(j,i))
		j++;
	k = max{k, i - j + 1};
}

具体实现

在整个具体实现过程中,关键在于 c h e c k ( j    ,    i ) check(j\;,\;i) check(j,i) 如何实现:

一般做法(笨办法)

//检查数组a[N]中是否存在重复元素。
bool check(int j, int i){
	for(int m = j; m < i; m++){
		for (int n = m; n < i; n++)
			if(a[n] == a[m])
				return true;
	}
	return false;
}

巧妙方法

要确定一个数组里是否存在重复元素,可以考虑先统计数组的每个元素出现的次数,如果有某个元素出现的次数大于1,说明该数组中存在重复元素。

bool check(int i, int j, int ){
	for(int i = 0;i < n; i++){
		s[a[i]]++;
		if(s[a[i]] > 1)
			return true;
	}
	return false;
}

完整实现代码

#include<iostream>
using namespace std;
const int N = 100010;

int a[N], s[N];
int n;
int main(){
    scanf("%d", &n);
    int k = 0;
    for(int i = 0; i < n; i++)  scanf("%d", &a[i]);
    for(int i = 0 , j = 0; i < n; i++){
        s[a[i]]++;
        while(s[a[i]] > 1){
            s[a[j]]--;
            j++;
        }
        k = max(k, i - j + 1);
    }
    printf("%d", k);
    return 0;
}

标签:非负,int,rbrack,lbrack,++,给定,长度,check,重复
来源: https://blog.csdn.net/dream_fun/article/details/122149290