今天的这道题是关于滑动窗口的题目
题目传送门
https://vjudge.net/problem/UVA-11572
题意
这道题目的题意比较简单明了,在一个长度为A的序列中选出一串没有相同元素的序列,问序列最长为几
滑动窗口+set
首先这道题可以使用滑动窗口解决,我们从序列的开头开始,使用left指针指向子序列的开头,right指针指向子序列的结尾(当然一开始他们都是指向0),然后我们将right向右移,并将right访问到的元素一一加入集合中,当我们发现集合中已经有这个元素的时候,我们就试图将left向右移来删除元素,直到没有冲突的元素后(说明left已经把这个元素给删了),我们就可以继续移动right指针了,这所有的操作都将持续到right碰到右边界。
这种操作相当于是有一个滑动的窗口在数组上滑动,所以呢我们又称这种操作叫做滑动窗口。
代码
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
| #include <iostream> #include <algorithm> #include <set> using namespace std;
int main(){ int T; while(cin>>T){ int n; while(T--){ cin>>n; int *a=new int[n+10]; set<int> s; for(int i=0;i<n;i++) cin>>a[i]; int left=0,right=0; int ans=0; while(right<n){ if(left>right) right=left; if(!s.count(a[right])) { s.insert(a[right]); right++; ans=max(ans,right-left); } else{ s.erase(a[left]); left++; } } cout << ans << endl; } } return 0; }
|
滑动窗口+map
我们有另一种方法也可以解决这个问题——使用map,我们可以先对这个数组进行一次遍历,找到每个数字上一次出现的位置(如果没有就设置成-1),然后我们仍然使用滑动窗口的方法,只要right指向的数字上一次出现的位置在left左边(也就是在窗口外面),我们就让right继续移动,如果在里面就将left右移来删除数字,仍然是正确的方法,虽然在速度上可能并不如第一种方法,不过相差不了多远
代码
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
| #include <iostream> #include <algorithm> #include <map> using namespace std;
int main(){ int T; while(cin>>T){ int n; while(T--){ cin>>n; int *a=new int[n+10]; int *pre=new int[n+10]; map<int,int> mp; for(int i=0;i<n;i++) { cin >> a[i]; if(mp.count(a[i])) pre[i]=mp[a[i]]; else pre[i]=-1; mp[a[i]]=i; } int left=0,right=0,ans=0; while(right<n){ if(pre[right]<left){ right++; ans=max(ans,right-left); } else left++; } cout << ans << endl; } } return 0; }
|