一、题意
输入一个长度为n的序列A(n<=1000000),找到一个尽量长的连续子序列,使得序列中没有相同的元素。
二、解析
使用双指针法。或者叫滑动窗口法。
维护一个没有重复元素的窗口,即用i,j分别指向序列中当前窗口的头和尾,为了防止混乱,规定为A[i,j)。然后还需要一个vis数组表示元素是否已经在窗口中了。
我们每次尝试增加j,若A[j]不在窗口中,则可以放心的扩大窗口,并更新答案;若A[j]已经在窗口中出现过了,则我们只好增加i,并逐个从头排出元素,直到把和A[j]相同的那个元素排出来位置(这个元素是一定存在的,只要vis维护正确)。然后更新ans。
就这样直到j达到了n,说明窗口已经滑动到最右端了,已经把所有情况可能的最长子序列尝试过了。
由于i从头到尾只遍历一次,因此时间复杂度为O(n),不会超时。
三、代码
#include <iostream> #include <cstring> #include <cmath> using namespace std; const int maxn = 1000000 + 5; int kase, n, a[maxn], vis[maxn]; int main() { cin >> kase; while(kase --) { cin >> n; memset(vis, 0, sizeof(int) * n); for(int i = 0; i < n; i ++) cin >> a[i]; int i = 0, j = 0, ans = 0; while(j < n) { if(!vis[a[j]]) vis[a[j]] = 1, j ++, ans = max(ans, j - i); else { while(i < j && a[i] != a[j]) vis[a[i]] = 0, i ++; i ++, j ++, ans = max(ans, j - i); } } cout << ans << endl; } }
四、归纳
- 滑动窗口法,即用i,j双指针(一般用下标)对数组进行遍历,同时要需要一个额外的比如vis数组维护窗口中的元素信息,是一种O(n)的扫描方法。
原题叫 “唯一的雪花”,听起来感觉听凄美的呢... 虽然从来没有仔细看过原题的情景就是了hhhh