题目描述(传送门)
题目翻译:
对于序列(a1,a2,…,an),可以用(al,al+1,…,ar)来表示其子序列,其中l表示左边界,r表示有边界(1 ≤ l ≤ r ≤ n)。比如对于序列(1 4 2 8 5 7)及边界l = 2, r = 4,则子序列为(4 2 8)。当两个序列的l和r中有一个不相同时,两个序列就被认为是不相同的序列。
注意,即使两个子序列的元素相同且排列顺序一样,但只要l和r有一个不相同,两个子序列就是不相同的。比如有序列(1 1 1 1 1)以及两个子序列:l=1,r=3和l=2,r=4。虽然两个子序列的内容都是(1 1 1),但是这两个子序列是不相同的,因为它们的l,r不一样。
现在你需要从一个序列中找出两个长度均为k的子序列(可存在重叠部分)。但是这两个子序列必须是harmonious(和谐的?),即对于某些下标i(1≤i≤k),两个子序列的第i个元素必须相同。比如(1 7 3)和(4 7 8)是harmonious的,因为第2个元素都是7。而(1 2 3)和(3 1 2)不是harmonious的。
请你求出满足上述条件(1、两个子序列的l或r不相同;2、harmonious)的两个子序列的最大长度。
解题思路:
首先可以确定的是,两个子序列只要有一个位置的元素相同即可。题目又要求两个子序列在原序列的位置不能完全重合,则我们需要在原序列中找到两个相同的元素,再分别以这两个元素为基准,找到尽可能长的两个等长的子序列。如果我们找到了两个相同元素的位置,怎么确定子序列的长度呢?比如序列(1 2 3 4 5 6 7 3 9),可以找到两个3,那么子序列的长度显然是3+1=4,懂的话可以跳过下面的啰嗦
暂且抛开题目的限制,设两个子序列为(1 2 3 4 5 6 7 3 9)、(1 2 3 4 5 6 7 3 9)。然后开始加题目的限制,为使两个3的下标一致,则第二个子序列需要删去前面的1 2 3 4 5,注意子序列是连续的,则两个子序列变为(1 2 3 4 5 6 7 3 9)、(6 7 3 9)。为使两个子序列长度相同,则需要删除第一个序列后面的5个元素,两个子序列变为(1 2 3 4 )、(6 7 3 9),最后子序列的长度就是4。
简单地说就是第一个3的位置决定子序列的前半段长度,第二个3决定子序列的后半段长度,即ans=i+n-j=n-(j-i),其中i是第一个元素的下标,j是第二个元素的下标,i<j,n是原序列长度
那么在什么情况下,子序列的长度才最长呢?通过上面的公式ans=n-(j-i) 可以知道,当j-i最小时,结果最大,即两个元素越接近,子序列长度越长。而与两个元素具体是在原序列的中间还是一端没有关系,当时的我就是下意识地认为越中间越好,而忽略了关键是要两个元素尽可能地靠近。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 200010
using namespace std;
int main(){
// freopen("1.txt","r",stdin);
int t,n,l,r,book[N],a,maxn,d;
cin>>t;
while(t--){
cin>>n;
memset(book,0,sizeof(book));
maxn=-1;
for(int i=1;i<=n;i++){
cin>>a;
if(book[a]==0) book[a]=i;//如果这个数字是第一次出现,则标记其位置
else{//不是第一个出现
//book[a]是这个数字上一次出现的位置,book[a]+n-i等同于上面分析中的ans=i+n-j
maxn=max(maxn,book[a]+n-i);
//更新位置。因为只考虑最接近的两个数字,所以只需要保存该数字上一次出现的位置,而不管再之前的
book[a]=i;
}
}
cout<<maxn<<endl;
}
return 0;
}
总结:
正确分析更重要,不要没想明白就开始敲代码。