题意:定义k-bag就是若干个1到k的序列组成的序列,每一个1~k序列只能包含k个数,也就是不能重复,列如 1 2 3 2 3 1 3 2 1 叫做一个 3 - bag。而k-bag的一段连续子序列又叫做part k-bag。现在给定你n个数和k,问你这n个数是否是一个 part k-bag。
思路:我们首先用一个len数组来确定,从当前位置开始的不重复序列的长度是多少。这样遇到当前len[i] == k 的位置就说明是给出序列中间部分能够形成k个不重复的的序列,这样每次递增k的长度 如果一直len的长度都等于k说明是可行的。
因为给出的序列是不全的,所以除了中间完整的部分,前后都有可能还有部分需要补充才能形成一个k序列,因此我们需要把前面不圈的部分也当做起点枚举,因为我们也不知道前几个不全的能组成一个完整的k序列,所以枚举起点到和开头位置第一次重复的点的位置,因为再往后面走,前面的就不可能形成k序列了,而当一个点的位置加上len大于n时,就说明从这个点开始后面都没有重复的点,那这个部分也可以补全为一个k序列。
这道题k <= 1e9,如果用一个数组记录当前的数出没出现过是不显示的,所以需要离散化一下给出的a数组
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e5+7;
int a[MAXN],b[MAXN],len[MAXN],pre[MAXN];
int n,k;
void lisan()
{
for(int i = 1;i <= n;i ++){
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b+1,b+1+n);
int cnt = unique(b+1,b+1+n)-b-1;
for(int i = 1;i <= n;i ++){
a[i] = lower_bound(b+1,b+1+cnt,a[i])-b;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
lisan();
int pos = 1,flag = 0;
for(int i = 1;i <= n;i ++){
while(!pre[a[pos]] && pos <= n)
++pre[a[pos]],pos++;//一直没有出现过 就是一直都没有重复就往后加就行了
--pre[a[i]];//当了前位置 把这个位置的减掉后面的相同的才可以进来
len[i] = pos - i;
}
int ed = min(k,len[1]+1);//来确定起点有几种可能性 要么与开头第一个重复,要么就是k 超过k也没有意义了
for(int i = 1;i <= ed;i ++){
int f = 1;
for(int j = i;j <= n;j += k){
if(j + len[j] > n) continue;
else if(len[j] != k){
f = 0;//这个起点不可行
break;
}
}
if(f == 1){
//找到了一个可行的起点 就完成了
flag = 1;
break;
}
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}