问题 D: subnum
【问题描述】
ROBIN喜欢只含有不同数的序列。现在给出含有N个正整数的一行序列,由于这N个数中可能有重复的数,ROBIN就不喜欢。但ROBIN可以删除前面一些数,或删除后面一些数,使保留下来的一段序列不再含有重复的数。ROBIN的任务是求出这个序列中最长的连续的没有重复数的子序列, 输出这个子序列的长度。
【输入格式】
第一行: 一个整数 N (0<=N<=100000)
第二行: 有N个正整数,每个正整数小与10^9。
【输出格式】
共一行, 一个整数,表示这个序列中最长的连续的没有重复数的子序列的长度。
【输入样例1】
1
45
【输出样例1】
1
【输入样例2】
8
2 3 4 3 5 7 7 3
【输出样例2】
4
【数据范围】
100%的数据 N 小于100000, 序列中的数小于10^9 。
这一题我首先想的是三重循环,一层枚举长度n~1,二层枚举起点,三层枚举检查。后来觉得枚举长度检查的话会超时(其实用这种方法不枚举长度也会),想了一下。如果用数组储存会爆(109),但其实一共只有105个数,emmm离散化打出来!!!而即使是用这种方法,那层循环还是免不了的,而想到了应该只是用O(n)的算法:双指针!!
正解。
先来一个离散化的普及:
(这位大大写的超级好,没有经过同意,万分抱歉,侵删)链接: 离散化:两种离散化方式详解.
在这里摘录全部 一部分(真的是一部分好吧) :
引入
离散化,就是把一些很离散的点给重新分配。
举个例子,如果一个坐标轴很长(>1e10),给你1e4个坐标,询问某一个点,坐标比它小的点有多少。
很容易就知道,对于1e4个点,我们不必把他们在坐标轴上的位置都表示出来,因为我们比较有多少比它小的话,只需要知道他们之间的相对大小就可以,而不是绝对大小,这,就需要离散化。
而离散化又分为两种,分为的两种是对于重复元素来划分的。第一种是重复元素离散化后的数字相同,第二种就是不同。
第一种
其实就是用一个辅助的数组把你要离散的所有数据存下来。
然后排序
去重,因为我们要保证相同的元素离散化后数字相同。
注意事项:
1.去重并不是把数组中的元素删去,而是重复的部分元素在数组末尾,去重之后数组的大小要减一
2.二分的时候,注意二分的区间范围,一定是离散化后的区间
3.如果需要多个数组同时离散化,那就把这些数组中的数都用数组存下来
第二种
第二种方式其实就是排序之后,枚举着放回原数组
原文链接:https://blog.csdn.net/weixin_43061009/article/details/82083983(再贴一遍)
emmm,因为老师讲的和dalao的代码不太一样,所以(我要贴我老湿的代码,闭嘴 )
先贴一下过程
(我也不知到为什么我要把第二种贴在前面 )
//这个是第二种#include<bits/stdc++.h>
using namespace std;
const int N= 1e5+5;
struct dt{
int x, id;
}a[N];
bool cmp(dt a, dt b){
if(a.x==b.x) return a.id< a.id;
return a.x<b.x;
}
int c1[N],c2[N], H[N], n;
int main()
{
cin>> n;
for(int i=1; i<=n; ++i) {
cin>> a[i].x;
a[i].id=i;
}
sort(a+1, a+n+1, cmp);
for(int i=1; i<=n; ++i){
c1[a[i].id]= i;
}
再来一个第一种(为什么现在才贴 )
#include<bits/stdc++.h>
using namespace std;
const int N= 1e5+5;
struct dt{
int x, id;
}a[N];
bool cmp(dt a, dt b){
if(a.x==b.x) return a.id< a.id;
return a.x<b.x;
}
int c1[N],c2[N], H[N], n;
int main()
{
cin>> n;
for(int i=1; i<=n; ++i) {
cin>> a[i].x;
a[i].id=i;
}
sort(a+1, a+n+1, cmp);
int t=0;
for(int i=1; i<=n; ++i){
if(a[i].x!=a[i-1].x) t++;
c2[a[i].id]= t;
}
keke就酱(这题我随便打了个伪哈希就过了 )
接下来是双指针(两个指针i,j 一起动)
一般思路好像是达到条件前指针就往前,达不到后指针就往前,直到达到条件为止
这题有个神仙方法(应该是算法)如下
先来一个图解
int ans=1;
int L=1,R=1;
while(R<=n){
if(H[c2[R]]>=L){
ans=max(ans, R-L);
L=H[c2[R]]+1;
H[c2[R]]=R;
R++;
}
else {
H[c2[R]]=R;
R++;
}
}
ans=max(ans, R-L);
cout<< ans;
现在来讲几个点
之前我一直都是写l<r的,但是老师说一定要写l<=r(一定要交叉(你说的都对 ))
然后就是匪夷所思的c2数组和H数组
c[i]表示第i个数组离散化之后变成了啥样
h[i]表示i上一次出现的地方在哪里
然后,一看这个代码觉得眉清目秀的有没有??!!(没有!! )
那我打一下注释
int ans=1;//最大值
int L=1,R=1;//左指针右指针
while(R<=n){//循环条件
if(H[c2[R]]>=L){//上一次出现数组中第R个元素c2[R]的位置在左指针的右方(就是不满足条件了)
ans=max(ans, R-L);//更新最大值(不用-1,想一想)
L=H[c2[R]]+1;//直接跳到上一次出现的地方的右端一个
H[c2[R]]=R;//更新出现位置
R++; //右指针前进
}
else {
H[c2[R]]=R;//更新出现位置
R++;//右指针前进
}
}
ans=max(ans, R-L);//最后更新一次ans
cout<< ans;//输出
嗯,舅酱
AC代码如下(这是标程)
#include<bits/stdc++.h>
using namespace std;
const int N= 1e5+5;
struct dt{
int x, id;
}a[N];
bool cmp(dt a, dt b){
if(a.x==b.x) return a.id< a.id;
return a.x<b.x;
}
int c1[N],c2[N], H[N], n;
int main()
{
cin>> n;
for(int i=1; i<=n; ++i) {
cin>> a[i].x;
a[i].id=i;
}
sort(a+1, a+n+1, cmp);
for(int i=1; i<=n; ++i){
c1[a[i].id]= i;
}
int t=0;
for(int i=1; i<=n; ++i){
if(a[i].x!=a[i-1].x) t++;
c2[a[i].id]= t;
}
int ans=1;
int L=1,R=1;
while(R<=n){
if(H[c2[R]]>=L){
ans=max(ans, R-L);
L=H[c2[R]]+1;
H[c2[R]]=R;
R++;
}
else {
H[c2[R]]=R;
R++;
}
}
ans=max(ans, R-L);
cout<< ans;
return 0;
}
今天就到这里,有时间我会把那个离散化的再补一下,反正这个题很有意思就对了。我绝对会及时完成。(真* )
在这里!!
过程大概就是这个样子